import express from 'express'
import cors from 'cors'
import fs from 'fs/promises'
import { createWriteStream } from 'fs'
import path from 'path'
import { WebSocketServer, WebSocket } from 'ws'
import { fileURLToPath } from 'url'
import simpleGit from 'simple-git'
import { spawn } from 'child_process'
import { NodeSSH } from 'node-ssh'
import archiver from 'archiver'
import { Writable } from 'stream'
import multer from 'multer'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const app = express()
const PORT = 3001
const DB_PATH = path.join(__dirname, '..', 'public', 'default.json')
const PROOFREAD_DB_PATH = path.join(__dirname, 'proofread_status.json')
const CONFIG_PATH = path.join(__dirname, 'config.json')
const GIT_CONFIG_PATH = path.join(__dirname, 'git_config.json')
const SYNC_CONFIG_PATH = path.join(__dirname, 'sync_config.json')

// 中间件
app.use(cors())
app.use(express.json())

// 配置 multer 用于文件上传
const upload = multer({ 
  storage: multer.memoryStorage(),
  limits: { fileSize: 50 * 1024 * 1024 } // 限制文件大小为 50MB
})

// 翻译文件路径
const TRANSLATION_FILE = path.join(__dirname, '..', 'default.json')

// Helper function to read config
async function getConfig() {
    const configContent = await fs.readFile(CONFIG_PATH, 'utf-8')
    return JSON.parse(configContent)
}

// Helper functions for sync config
async function getSyncConfig() {
    try {
        const configContent = await fs.readFile(SYNC_CONFIG_PATH, 'utf-8')
        return JSON.parse(configContent)
    } catch (error) {
        if (error.code === 'ENOENT') {
            // 如果文件不存在，返回默认配置
            return {
                projectPath: '',
                targetPath: ''
            }
        }
        throw error
    }
}

async function saveSyncConfig(config) {
    await fs.writeFile(SYNC_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8')
}

// 检测是否包含中文字符的函数
function containsChinese(text) {
    if (!text || typeof text !== 'string') {
        return false;
    }
    // 中文字符的Unicode范围
    const chineseRegex = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3000-\u303f]/;
    return chineseRegex.test(text);
}

app.get('/api/config', async (req, res) => {
    try {
        const config = await getConfig()
        res.json({ success: true, config })
    } catch (error) {
        res.status(500).json({ success: false, message: '无法读取配置文件' })
    }
})

app.post('/api/config', async (req, res) => {
    const { editorCount } = req.body;
    if (typeof editorCount !== 'number' || editorCount <= 0) {
        return res.status(400).json({ success: false, message: 'editorCount 必须是一个正数' });
    }
    try {
        // Recalculate and save assignments
        const fileContent = await fs.readFile(DB_PATH, 'utf-8');
        const data = JSON.parse(fileContent);
        const allItems = Object.values(data).map(item => ({ id: item.$id })).sort((a, b) => a.id - b.id);
        
        const totalItems = allItems.length;
        const itemsPerEditor = Math.ceil(totalItems / editorCount);
        const assignments = {};

        for (let i = 0; i < editorCount; i++) {
            const editorId = i + 1;
            const startIndex = i * itemsPerEditor;
            const endIndex = Math.min(startIndex + itemsPerEditor - 1, totalItems - 1);
            
            if (startIndex < totalItems) {
                assignments[editorId] = {
                    startId: allItems[startIndex].id,
                    endId: allItems[endIndex].id,
                };
            }
        }

        const newConfig = { editorCount, assignments };
        await fs.writeFile(CONFIG_PATH, JSON.stringify(newConfig, null, 2), 'utf-8');
        
        res.json({ success: true, message: '配置已更新，任务已重新分配并保存' });
    } catch (error) {
        console.error('Error updating config:', error);
        res.status(500).json({ success: false, message: '无法写入配置文件' });
    }
});

// 获取翻译数据（分页）
app.get('/api/translations', async (req, res) => {
  try {
    const fileContent = await fs.readFile(DB_PATH, 'utf-8')
    const data = JSON.parse(fileContent)

    const proofreadStatusContent = await fs.readFile(PROOFREAD_DB_PATH, 'utf-8')
    const proofreadData = JSON.parse(proofreadStatusContent)

    let allItems = Object.entries(data).map(([key, value]) => ({
      id: value.$id,
      chinese: key,
      translation: value['en-US'] || '',
      isProofread: proofreadData[value.$id] === true,
    })).sort((a, b) => a.id - b.id)

    const editorId = req.query.editorId ? parseInt(req.query.editorId) : null
    if (editorId) {
      const config = await getConfig()
      if (config.assignments && config.assignments[editorId]) {
        const { startId, endId } = config.assignments[editorId]
        allItems = allItems.filter(item => item.id >= startId && item.id <= endId)
      } else {
        allItems = []
      }
    }

    const proofreadStatusFilter = req.query.proofreadStatus || 'not_proofread'

    if (proofreadStatusFilter !== 'all') {
      const isFilteringProofread = proofreadStatusFilter === 'proofread'
      allItems = allItems.filter(item => item.isProofread === isFilteringProofread)
    }

    const startId = req.query.startId ? parseInt(req.query.startId) : null;
    const endId = req.query.endId ? parseInt(req.query.endId) : null;

    if (startId !== null && endId !== null) {
      allItems = allItems.filter(item => item.id >= startId && item.id <= endId);
    }

          if (req.query.search) {
        const searchTerm = String(req.query.search).toLowerCase().trim();
        if (searchTerm) {
          allItems = allItems.filter(item =>
            (item.chinese && item.chinese.toLowerCase().includes(searchTerm)) ||
            (item.id && item.id.toString().includes(searchTerm)) ||
            (item.translation && item.translation.toLowerCase().includes(searchTerm))
          );
        }
    }
    
    const page = parseInt(req.query.page) || 1
    const limit = parseInt(req.query.limit) || 20
    const startIndex = (page - 1) * limit
    const endIndex = page * limit

    const results = allItems.slice(startIndex, endIndex)
    
    res.json({
      success: true,
      data: results,
      totalCount: allItems.length,
      currentPage: page,
    })
  } catch (error) {
    console.error(error)
    res.status(500).json({ success: false, message: '服务器错误' })
  }
})

// 保存翻译修改（支持单个条目）
app.post('/api/translations/save', async (req, res) => {
  const { id, translation } = req.body

  if (!id) {
    return res.status(400).json({ success: false, message: 'ID is required' })
  }

  try {
    const fileContent = await fs.readFile(DB_PATH, 'utf-8')
    const data = JSON.parse(fileContent)
    
    let itemKey = null
    for (const [key, value] of Object.entries(data)) {
      if (value.$id === id) {
        itemKey = key
        break
      }
    }

    if (!itemKey) {
      return res.status(404).json({ success: false, message: 'ID not found' })
    }

    data[itemKey]['en-US'] = translation

    await fs.writeFile(DB_PATH, JSON.stringify(data, null, 2), 'utf-8')
    
    // 广播保存事件
    wss.clients.forEach(client => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify({
          type: 'translationSaved',
          id: id,
          translation: translation
        }))
      }
    })

    res.json({ success: true, message: '保存成功' })
  } catch (error) {
    console.error(error)
    res.status(500).json({ success: false, message: '服务器错误' })
  }
})

// 获取备份文件列表
app.get('/api/backups', async (req, res) => {
  try {
    const files = await fs.readdir(__dirname + '/..')
    const backupFiles = files
      .filter(file => file.startsWith('default.json.backup.'))
      .map(file => ({
        name: file,
        timestamp: parseInt(file.split('.').pop()),
        date: new Date(parseInt(file.split('.').pop())).toLocaleString('zh-CN')
      }))
      .sort((a, b) => b.timestamp - a.timestamp)

    res.json(backupFiles)
  } catch (error) {
    console.error('Error reading backups:', error)
    res.status(500).json({ error: '读取备份文件失败' })
  }
})

// 恢复备份
app.post('/api/restore/:filename', async (req, res) => {
  try {
    const { filename } = req.params
    const backupFile = path.join(__dirname, '..', filename)
    
    // 检查备份文件是否存在
    try {
      await fs.access(backupFile)
    } catch {
      return res.status(404).json({ error: '备份文件不存在' })
    }

    // 创建当前文件的备份
    const currentBackup = path.join(__dirname, '..', `default.json.backup.${Date.now()}`)
    await fs.copyFile(TRANSLATION_FILE, currentBackup)

    // 恢复备份
    await fs.copyFile(backupFile, TRANSLATION_FILE)
    
    // 同时更新public目录中的文件
    await fs.copyFile(backupFile, DB_PATH)

    res.json({ 
      success: true, 
      message: '备份恢复成功',
      currentBackup: path.basename(currentBackup)
    })
  } catch (error) {
    console.error('Error restoring backup:', error)
    res.status(500).json({ error: '恢复备份失败' })
  }
})

// 健康检查
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() })
})

app.post('/api/proofread-status', async (req, res) => {
  const { id, isProofread } = req.body

  if (id === undefined || isProofread === undefined) {
    return res.status(400).json({ success: false, message: 'ID and isProofread status are required' })
  }

  try {
    const proofreadStatusContent = await fs.readFile(PROOFREAD_DB_PATH, 'utf-8')
    const proofreadData = JSON.parse(proofreadStatusContent)

    if (isProofread) {
      proofreadData[id] = true
    } else {
      delete proofreadData[id]
    }

    await fs.writeFile(PROOFREAD_DB_PATH, JSON.stringify(proofreadData, null, 2), 'utf-8')

    // 广播精校状态变更事件
    wss.clients.forEach(client => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify({
          type: 'proofreadStatusChanged',
          id: id,
          isProofread: isProofread
        }))
      }
    })

    res.json({ success: true, message: '精校状态更新成功' })
  } catch (error) {
    console.error('Error updating proofread status:', error)
    res.status(500).json({ success: false, message: '服务器错误' })
  }
})

// --- Sync Configuration API ---

// 获取同步配置
app.get('/api/sync-config', async (req, res) => {
  try {
    const config = await getSyncConfig()
    res.json({ success: true, config })
  } catch (error) {
    console.error('Error reading sync config:', error)
    res.status(500).json({ success: false, message: '读取同步配置失败' })
  }
})

// 保存项目地址
app.post('/api/sync-config/project-path', async (req, res) => {
  const { projectPath } = req.body
  if (!projectPath) {
    return res.status(400).json({ success: false, message: '项目地址不能为空' })
  }

  try {
    const config = await getSyncConfig()
    config.projectPath = projectPath
    await saveSyncConfig(config)
    res.json({ success: true, message: '项目地址保存成功' })
  } catch (error) {
    console.error('Error saving project path:', error)
    res.status(500).json({ success: false, message: '保存项目地址失败' })
  }
})

// 保存目标路径
app.post('/api/sync-config/target-path', async (req, res) => {
  const { targetPath } = req.body
  if (!targetPath) {
    return res.status(400).json({ success: false, message: '目标路径不能为空' })
  }

  try {
    const config = await getSyncConfig()
    config.targetPath = targetPath
    await saveSyncConfig(config)
    res.json({ success: true, message: '目标路径保存成功' })
  } catch (error) {
    console.error('Error saving target path:', error)
    res.status(500).json({ success: false, message: '保存目标路径失败' })
  }
})

// 同步数据到目标项目
app.post('/api/sync-data', async (req, res) => {
  const { projectPath, targetPath } = req.body
  
  if (!projectPath || !targetPath) {
    return res.status(400).json({ success: false, message: '项目地址和目标路径都不能为空' })
  }

  // 广播日志的辅助函数
  const broadcast = (log) => {
    broadcastSyncLog(log);
    console.log(log);
  };

  try {
    // 构建完整的目标文件路径，确保路径分隔符正确
    const normalizedProjectPath = path.resolve(projectPath.trim())
    const normalizedTargetPath = targetPath.trim().replace(/^[\\\/]+|[\\\/]+$/g, '') // 去掉开头和结尾的斜杠
    const fullTargetPath = path.join(normalizedProjectPath, normalizedTargetPath, 'default.json')
    const targetDir = path.dirname(fullTargetPath)
    
    broadcast(`[SYNC] 开始同步数据...`)
    broadcast(`[SYNC] 源文件: ${DB_PATH}`)
    broadcast(`[SYNC] 项目根目录: ${normalizedProjectPath}`)
    broadcast(`[SYNC] 目标路径: ${normalizedTargetPath}`)
    broadcast(`[SYNC] 完整目标文件路径: ${fullTargetPath}`)
    
    // 检查源文件是否存在
    try {
      await fs.access(DB_PATH)
      broadcast(`[SYNC] ✅ 源翻译文件检查通过`)
    } catch (error) {
      broadcast(`[SYNC] ❌ 源翻译文件不存在`)
      return res.status(404).json({ success: false, message: '源翻译文件不存在' })
    }
    
    // 检查项目路径是否存在
    try {
      await fs.access(normalizedProjectPath)
      broadcast(`[SYNC] ✅ 目标项目路径检查通过`)
    } catch (error) {
      broadcast(`[SYNC] ❌ 目标项目路径不存在: ${normalizedProjectPath}`)
      return res.status(404).json({ success: false, message: `目标项目路径不存在: ${normalizedProjectPath}` })
    }
    
    // 创建目标目录（如果不存在）
    broadcast(`[SYNC] 正在创建目标目录...`)
    try {
      await fs.mkdir(targetDir, { recursive: true })
      broadcast(`[SYNC] ✅ 目标目录创建成功: ${targetDir}`)
    } catch (error) {
      broadcast(`[SYNC] ⚠️ 目标目录已存在或创建失败: ${error.message}`)
    }
    
    // 复制文件
    broadcast(`[SYNC] 正在复制翻译文件...`)
    try {
      await fs.copyFile(DB_PATH, fullTargetPath)
      broadcast(`[SYNC] ✅ 文件复制成功`)
    } catch (error) {
      broadcast(`[SYNC] ❌ 文件复制失败: ${error.message}`)
      return res.status(500).json({ success: false, message: `文件复制失败: ${error.message}` })
    }
    
    // 在目标项目根目录下执行编译命令
    broadcast(`[SYNC] 开始执行编译命令...`)
    broadcast(`[SYNC] 编译命令: npx voerkai18n compile`)
    broadcast(`[SYNC] 工作目录: ${normalizedProjectPath}`)
    broadcast(`[SYNC] 当前服务器目录: ${process.cwd()}`)
    broadcast(`[SYNC] 目标项目绝对路径: ${path.resolve(normalizedProjectPath)}`)
    try {
      const compileProcess = spawn('npx', ['voerkai18n', 'compile'], {
        cwd: normalizedProjectPath,
        shell: true,
        stdio: ['ignore', 'pipe', 'pipe'],
        env: {
          ...process.env,
          PWD: normalizedProjectPath,  // 明确设置PWD环境变量
          INIT_CWD: normalizedProjectPath  // 设置初始工作目录
        }
      })
      
      let stdout = ''
      let stderr = ''
      
      compileProcess.stdout.on('data', (data) => {
        const output = data.toString().trim()
        stdout += output
        broadcast(`[COMPILE] ${output}`)
      })
      
      compileProcess.stderr.on('data', (data) => {
        const output = data.toString().trim()
        stderr += output
        broadcast(`[COMPILE ERROR] ${output}`)
      })
      
      const compileResult = await new Promise((resolve, reject) => {
        compileProcess.on('close', (code) => {
          resolve({ code, stdout, stderr })
        })
        
        compileProcess.on('error', (error) => {
          reject(error)
        })
        
        // 设置60秒超时（编译可能需要更长时间）
        setTimeout(() => {
          compileProcess.kill()
          reject(new Error('编译命令执行超时'))
        }, 60000)
      })
      
      if (compileResult.code === 0) {
        broadcast(`[SYNC] ✅ 编译成功完成`)
        broadcast(`[SYNC] 🎉 同步任务全部完成！请刷新目标项目查看效果`)
        res.json({ 
          success: true, 
          message: '同步成功！翻译文件已更新并编译完成',
          compileOutput: compileResult.stdout
        })
      } else {
        broadcast(`[SYNC] ❌ 编译失败，退出码: ${compileResult.code}`)
        broadcast(`[SYNC] 文件同步成功，但编译过程出现问题`)
        res.json({ 
          success: false, 
          message: `文件同步成功，但编译失败（退出码: ${compileResult.code}）`,
          compileOutput: compileResult.stderr || compileResult.stdout
        })
      }
    } catch (error) {
      broadcast(`[SYNC] ❌ 编译命令执行失败: ${error.message}`)
      res.json({ 
        success: false, 
        message: `文件同步成功，但编译命令执行失败: ${error.message}`
      })
    }
    
  } catch (error) {
    broadcast(`[SYNC] ❌ 同步过程发生错误: ${error.message}`)
    console.error('Error syncing data:', error)
    res.status(500).json({ success: false, message: `同步失败: ${error.message}` })
  }
})

// 从目标项目同步数据到服务器
app.post('/api/sync-from-project', async (req, res) => {
  const { projectPath, targetPath } = req.body
  
  if (!projectPath || !targetPath) {
    return res.status(400).json({ success: false, message: '项目地址和目标路径都不能为空' })
  }

  // 广播日志的辅助函数
  const broadcast = (log) => {
    broadcastSyncLog(log);
    console.log(log);
  };

  try {
    // 构建完整的源文件路径（从目标项目）
    const normalizedProjectPath = path.resolve(projectPath.trim())
    const normalizedTargetPath = targetPath.trim().replace(/^[\\\/]+|[\\\/]+$/g, '') // 去掉开头和结尾的斜杠
    const sourceFilePath = path.join(normalizedProjectPath, normalizedTargetPath, 'default.json')
    
    broadcast(`[SYNC FROM PROJECT] 开始从目标项目同步数据...`)
    broadcast(`[SYNC FROM PROJECT] 源文件: ${sourceFilePath}`)
    broadcast(`[SYNC FROM PROJECT] 目标文件: ${DB_PATH}`)
    
    // 检查源文件是否存在
    try {
      await fs.access(sourceFilePath)
      broadcast(`[SYNC FROM PROJECT] ✅ 源文件检查通过`)
    } catch (error) {
      broadcast(`[SYNC FROM PROJECT] ❌ 源文件不存在: ${sourceFilePath}`)
      return res.status(404).json({ success: false, message: `源文件不存在: ${sourceFilePath}` })
    }
    
    // 检查项目路径是否存在
    try {
      await fs.access(normalizedProjectPath)
      broadcast(`[SYNC FROM PROJECT] ✅ 项目路径检查通过`)
    } catch (error) {
      broadcast(`[SYNC FROM PROJECT] ❌ 项目路径不存在: ${normalizedProjectPath}`)
      return res.status(404).json({ success: false, message: `项目路径不存在: ${normalizedProjectPath}` })
    }
    
    // 备份当前服务器文件
    const backupPath = `${DB_PATH}.backup.${Date.now()}`
    try {
      await fs.copyFile(DB_PATH, backupPath)
      broadcast(`[SYNC FROM PROJECT] ✅ 当前文件已备份到: ${backupPath}`)
    } catch (error) {
      broadcast(`[SYNC FROM PROJECT] ⚠️ 备份当前文件失败，继续执行: ${error.message}`)
    }
    
    // 复制文件从目标项目到服务器
    broadcast(`[SYNC FROM PROJECT] 正在复制文件...`)
    try {
      await fs.copyFile(sourceFilePath, DB_PATH)
      broadcast(`[SYNC FROM PROJECT] ✅ 文件复制成功`)
    } catch (error) {
      broadcast(`[SYNC FROM PROJECT] ❌ 文件复制失败: ${error.message}`)
      return res.status(500).json({ success: false, message: `文件复制失败: ${error.message}` })
    }
    
    // 验证复制的文件是否为有效的 JSON
    try {
      const fileContent = await fs.readFile(DB_PATH, 'utf-8')
      JSON.parse(fileContent)
      broadcast(`[SYNC FROM PROJECT] ✅ 文件格式验证通过`)
    } catch (error) {
      broadcast(`[SYNC FROM PROJECT] ❌ 复制的文件不是有效的 JSON 格式`)
      // 如果有备份文件，尝试恢复
      try {
        await fs.copyFile(backupPath, DB_PATH)
        broadcast(`[SYNC FROM PROJECT] ✅ 已从备份文件恢复`)
      } catch (restoreError) {
        broadcast(`[SYNC FROM PROJECT] ❌ 恢复备份文件失败: ${restoreError.message}`)
      }
      return res.status(400).json({ success: false, message: '复制的文件不是有效的 JSON 格式' })
    }
    
    broadcast(`[SYNC FROM PROJECT] 🎉 从目标项目同步完成！数据已更新`)
    res.json({ 
      success: true, 
      message: '从目标项目同步成功！翻译数据已更新'
    })
    
  } catch (error) {
    broadcast(`[SYNC FROM PROJECT] ❌ 同步过程发生错误: ${error.message}`)
    console.error('Error syncing from project:', error)
    res.status(500).json({ success: false, message: `同步失败: ${error.message}` })
  }
})

// 筛选包含中文的翻译内容并提供下载
app.post('/api/filter-chinese', async (req, res) => {
  try {
    console.log('[FILTER] 开始筛选包含中文的翻译内容...')
    
    // 读取翻译文件
    const fileContent = await fs.readFile(DB_PATH, 'utf-8')
    const data = JSON.parse(fileContent)
    
    console.log(`[FILTER] 成功读取翻译文件，共 ${Object.keys(data).length} 条记录`)
    
    let filteredData = {}
    let filteredCount = 0
    let totalCount = 0
    
    // 遍历所有条目
    for (const [chineseKey, item] of Object.entries(data)) {
      totalCount++
      
      const enUsValue = item['en-US']
      
      // 检查 en-US 字段是否包含中文
      if (containsChinese(enUsValue)) {
        filteredData[chineseKey] = item
        filteredCount++
      }
    }
    
    console.log(`[FILTER] ✅ 筛选完成，发现 ${filteredCount} 条包含中文的记录`)
    
    if (filteredCount > 0) {
      // 设置响应头，提供文件下载
      const filename = `chinese_content_${new Date().toISOString().slice(0, 10)}.json`
      res.setHeader('Content-Disposition', `attachment; filename="${filename}"`)
      res.setHeader('Content-Type', 'application/json')
      res.send(JSON.stringify(filteredData, null, 4))
      
      console.log(`[FILTER] 🎉 文件已生成并发送下载: ${filename}`)
    } else {
      console.log('[FILTER] ⚠️ 未发现包含中文的翻译条目')
      res.json({ success: false, message: '未发现包含中文的翻译条目' })
    }
    
  } catch (error) {
    console.error(`[FILTER] ❌ 筛选过程发生错误: ${error.message}`)
    res.status(500).json({ success: false, message: `筛选失败: ${error.message}` })
  }
})

// 上传文件更新翻译数据
app.post('/api/upload-update', upload.single('file'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ success: false, message: '未找到上传的文件' })
    }
    
    console.log(`[UPLOAD] 开始处理上传的文件: ${req.file.originalname}`)
    
    // 解析上传的文件内容
    let uploadedData
    try {
      uploadedData = JSON.parse(req.file.buffer.toString('utf-8'))
      console.log(`[UPLOAD] 文件解析成功，包含 ${Object.keys(uploadedData).length} 条记录`)
    } catch (parseError) {
      console.log('[UPLOAD] ❌ 文件格式错误，不是有效的 JSON 文件')
      return res.status(400).json({ success: false, message: '文件格式错误，请上传有效的 JSON 文件' })
    }
    
    // 读取当前服务器的翻译文件
    const currentData = JSON.parse(await fs.readFile(DB_PATH, 'utf-8'))
    console.log(`[UPLOAD] 当前服务器文件包含 ${Object.keys(currentData).length} 条记录`)
    
    let updatedCount = 0
    let notFoundCount = 0
    let noIdCount = 0
    
    // 遍历上传文件中的每个条目
    for (const [chineseKey, item] of Object.entries(uploadedData)) {
      const id = item.$id
      
      if (!id) {
        noIdCount++
        continue
      }
      
      // 查找当前数据中对应的条目
      let targetKey = null
      for (const [key, targetItem] of Object.entries(currentData)) {
        if (targetItem.$id === id) {
          targetKey = key
          break
        }
      }
      
      if (targetKey) {
        // 更新翻译
        currentData[targetKey]['en-US'] = item['en-US']
        updatedCount++
      } else {
        notFoundCount++
      }
    }
    
    // 写入更新后的文件
    if (updatedCount > 0) {
      console.log('[UPLOAD] 正在保存更新后的文件...')
      await fs.writeFile(DB_PATH, JSON.stringify(currentData, null, 4), 'utf-8')
      console.log('[UPLOAD] ✅ 文件保存成功')
    }
    
    // 输出统计信息
    console.log(`[UPLOAD] 🎉 更新完成！成功更新: ${updatedCount} 条`)
    
    res.json({ 
      success: true, 
      message: `成功更新 ${updatedCount} 条记录`,
      stats: {
        updated: updatedCount,
        notFound: notFoundCount,
        noId: noIdCount,
        total: Object.keys(uploadedData).length
      }
    })
    
  } catch (error) {
    console.error(`[UPLOAD] ❌ 上传更新过程发生错误: ${error.message}`)
    res.status(500).json({ success: false, message: `上传更新失败: ${error.message}` })
  }
})

// 下载目标项目的 default.json
app.post('/api/download-project-json', async (req, res) => {
  try {
    const { projectPath, targetPath } = req.body
    
    if (!projectPath || !targetPath) {
      return res.status(400).json({ success: false, message: '项目地址和目标路径不能为空' })
    }
    
    console.log(`[DOWNLOAD] 准备下载目标项目的 default.json`)
    console.log(`[DOWNLOAD] 项目路径: ${projectPath}`)
    console.log(`[DOWNLOAD] 目标路径: ${targetPath}`)
    
    // 构建完整路径
    const fullPath = path.join(projectPath, targetPath, 'default.json')
    console.log(`[DOWNLOAD] 完整文件路径: ${fullPath}`)
    
    // 检查文件是否存在
    try {
      await fs.access(fullPath)
    } catch (error) {
      console.log(`[DOWNLOAD] ❌ 文件不存在: ${fullPath}`)
      return res.status(404).json({ success: false, message: '目标项目中未找到 default.json 文件' })
    }
    
    // 读取文件内容
    const fileContent = await fs.readFile(fullPath, 'utf-8')
    console.log(`[DOWNLOAD] ✅ 成功读取文件，大小: ${fileContent.length} 字符`)
    
    // 设置响应头，让浏览器下载文件
    res.setHeader('Content-Type', 'application/json')
    res.setHeader('Content-Disposition', 'attachment; filename=default.json')
    res.send(fileContent)
    
    console.log(`[DOWNLOAD] 🎉 目标项目的 default.json 下载完成`)
    
  } catch (error) {
    console.error(`[DOWNLOAD] ❌ 下载目标项目文件时发生错误: ${error.message}`)
    res.status(500).json({ success: false, message: `下载失败: ${error.message}` })
  }
})

// 下载服务器的 default.json
app.get('/api/download-server-json', async (req, res) => {
  try {
    console.log(`[DOWNLOAD] 准备下载服务器的 default.json`)
    console.log(`[DOWNLOAD] 服务器文件路径: ${DB_PATH}`)
    
    // 检查文件是否存在
    try {
      await fs.access(DB_PATH)
    } catch (error) {
      console.log(`[DOWNLOAD] ❌ 服务器文件不存在: ${DB_PATH}`)
      return res.status(404).json({ success: false, message: '服务器上未找到 default.json 文件' })
    }
    
    // 读取文件内容
    const fileContent = await fs.readFile(DB_PATH, 'utf-8')
    console.log(`[DOWNLOAD] ✅ 成功读取服务器文件，大小: ${fileContent.length} 字符`)
    
    // 设置响应头，让浏览器下载文件
    res.setHeader('Content-Type', 'application/json')
    res.setHeader('Content-Disposition', 'attachment; filename=default.json')
    res.send(fileContent)
    
    console.log(`[DOWNLOAD] 🎉 服务器的 default.json 下载完成`)
    
  } catch (error) {
    console.error(`[DOWNLOAD] ❌ 下载服务器文件时发生错误: ${error.message}`)
    res.status(500).json({ success: false, message: `下载失败: ${error.message}` })
  }
})

// --- Git Management API ---

// Helper to read/write git config
const readGitConfig = async () => {
  try {
    const data = await fs.readFile(GIT_CONFIG_PATH, 'utf-8')
    const config = JSON.parse(data)
    // Ensure defaults for new fields
    if (!config.remarks) {
      config.remarks = {}
    }
    if (!config.favoriteBranches) {
      config.favoriteBranches = []
    }
    if (!config.productionBranch) {
      config.productionBranch = ''
    }
    if (!config.sshConfig) {
      config.sshConfig = {
        host: '',
        port: 22,
        username: '',
        privateKeyPath: 'public/blacktom-deploy',
        remotePath: '',
        buildDir: 'dist',
        defaultPerms: '755',
        owner: 'www:www'
      }
    }
    return config
  } catch (error) {
    // If file doesn't exist, return default structure
    if (error.code === 'ENOENT') {
      return { 
        repoPath: '', 
        remarks: {}, 
        favoriteBranches: [],
        productionBranch: '',
        sshConfig: {
          host: '',
          port: 22,
          username: '',
          privateKeyPath: 'public/blacktom-deploy',
          remotePath: '',
          buildDir: 'dist',
          defaultPerms: '755',
          owner: 'www:www'
        }
      }
    }
    throw error
  }
}

// --- Global State & Class for Deployment ---
let currentDeployment = null;

class Deployment {
    constructor(branch, broadcast) {
        this.branch = branch;
        this.broadcast = broadcast;
        this.ssh = new NodeSSH();
        this.childProcess = null;
        this.cancelled = false;
        this.remoteTempPath = '';
        this.nonConflicts = [];
        this.permissionCache = new Map();
        this.heartbeatInterval = null;
        this.deploymentTimeout = null;
        this.waitingForUserConfirmation = false;
    }
    
    end() {
        if (this.heartbeatInterval) {
            clearInterval(this.heartbeatInterval);
            this.heartbeatInterval = null;
        }
        if (this.deploymentTimeout) {
            clearTimeout(this.deploymentTimeout);
            this.deploymentTimeout = null;
        }
        if (this.ssh.connection) this.ssh.dispose();
        currentDeployment = null;
    }

    cancel() {
        this.cancelled = true;
        if (this.heartbeatInterval) {
            clearInterval(this.heartbeatInterval);
            this.heartbeatInterval = null;
        }
        if (this.deploymentTimeout) {
            clearTimeout(this.deploymentTimeout);
            this.deploymentTimeout = null;
        }
        if (this.childProcess) this.childProcess.kill('SIGTERM');
        this.broadcast(`[CANCEL] 部署 '${this.branch}' 已被取消。`);
        this.end();
    }
}

const writeGitConfig = async (config) => {
  await fs.writeFile(GIT_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8')
}

// Get Git config
app.get('/api/git/config', async (req, res) => {
  try {
    const config = await readGitConfig()
    res.json({ success: true, data: config })
  } catch (error) {
    res.status(500).json({ success: false, message: '读取Git配置失败' })
  }
})

// General purpose git config update
app.post('/api/git/config', async (req, res) => {
  try {
    const newConfigData = req.body;
    const config = await readGitConfig();

    // Merge new data into existing config
    const updatedConfig = { ...config, ...newConfigData };
    
    // Deeper merge for nested objects like sshConfig
    if (newConfigData.sshConfig) {
      updatedConfig.sshConfig = { ...config.sshConfig, ...newConfigData.sshConfig };
    }

    await writeGitConfig(updatedConfig);
    
    broadcastToGitClients({
      type: 'git_event',
      event: 'config_updated',
      config: updatedConfig
    });

    res.json({ success: true, message: '配置已更新', data: updatedConfig });
  } catch (error) {
    console.error('Error updating config:', error);
    res.status(500).json({ success: false, message: '更新配置时出错' });
  }
});

// Save Git repo path
app.post('/api/git/repo', async (req, res) => {
  const { repoPath } = req.body
  if (!repoPath) {
    return res.status(400).json({ success: false, message: '项目路径不能为空' })
  }

  try {
    // Validate path
    const git = simpleGit(repoPath)
    if (!(await git.checkIsRepo())) {
      return res.status(400).json({ success: false, message: '提供的路径不是一个有效的Git仓库' })
    }

    const config = await readGitConfig()
    config.repoPath = repoPath
    await writeGitConfig(config)
    res.json({ success: true, message: '项目路径已保存' })
  } catch (error) {
    console.error('Error saving git repo path:', error)
    res.status(500).json({ success: false, message: '保存Git项目路径时出错' })
  }
})

// Get branches
app.get('/api/git/branches', async (req, res) => {
  try {
    const { repoPath } = await readGitConfig()
    if (!repoPath) {
      return res.status(400).json({ success: false, message: '请先配置Git项目路径' })
    }
    const git = simpleGit(repoPath)
    await git.fetch() // Fetch latest branches info
    const branchSummary = await git.branch(['-a'])

    const config = await readGitConfig()

    // Clean up branch names and remove duplicates
    const allBranchNames = branchSummary.all
      .filter(name => !name.includes('->')) // Filter out HEAD pointers
      .map(name => name.replace(/^remotes\/[^\/]+\//, ''))
      
    const uniqueBranchNames = [...new Set(allBranchNames)];

    const branches = uniqueBranchNames.map(branchName => ({
      name: branchName,
      isCurrent: branchSummary.current === branchName,
      remark: config.remarks[branchName] || '',
      isFavorite: config.favoriteBranches.includes(branchName)
    }))

    res.json({ success: true, data: { branches, current: branchSummary.current, productionBranch: config.productionBranch } })
  } catch (error) {
    console.error('Error getting branches:', error)
    res.status(500).json({ success: false, message: '获取分支列表时出错' })
  }
})

// Checkout branch
app.post('/api/git/checkout', async (req, res) => {
  const { branch } = req.body
  if (!branch) {
    return res.status(400).json({ success: false, message: '分支名不能为空' })
  }

  try {
    const { repoPath } = await readGitConfig()
    if (!repoPath) {
      return res.status(400).json({ success: false, message: '请先配置Git项目路径' })
    }
    const git = simpleGit(repoPath)
    await git.checkout(branch)

    // Broadcast event
    broadcastToGitClients({
      type: 'git_event',
      event: 'checkout',
      branch: branch,
      message: `项目已切换到分支: ${branch}`,
    })

    res.json({ success: true, message: `已切换到分支 ${branch}` })
  } catch (error) {
    console.error('Error checking out branch:', error)
    let detailedMessage = '切换分支时发生未知错误。';
    if (error.message) {
        if (error.message.includes('overwritten')) {
            detailedMessage = '操作失败：您有未提交的更改与目标分支冲突，请先提交或暂存您的更改。';
        } else if (error.message.includes('pathspec')) {
            detailedMessage = `操作失败：分支 "${branch}" 不存在或名称有误。`;
        } else {
            const stderrMatch = error.message.match(/stderr: (.*)/);
            detailedMessage = stderrMatch ? stderrMatch[1].trim() : error.message;
        }
    }
    res.status(500).json({ success: false, message: detailedMessage })
  }
})

// Pull changes
app.post('/api/git/pull', async (req, res) => {
  const { branch } = req.body;
  if (!branch) {
    return res.status(400).json({ success: false, message: '分支名不能为空' });
  }

  try {
    const { repoPath } = await readGitConfig()
    if (!repoPath) {
      return res.status(400).json({ success: false, message: '请先配置Git项目路径' })
    }
    const git = simpleGit(repoPath)

    // First, checkout to the desired branch. This also handles cases where it's already the current branch.
    await git.checkout(branch);
    
    // Broadcast the checkout event so other clients can update their state
    broadcastToGitClients({
      type: 'git_event',
      event: 'checkout',
      branch: branch,
      message: `项目已切换到分支: ${branch}`,
    });

    // Then, pull the changes for that branch.
    const pullResult = await git.pull()
    
    res.json({ success: true, message: `拉取成功`, data: pullResult, branch: branch })
  } catch (error) {
    console.error('Error pulling changes:', error)
    res.status(500).json({ success: false, message: '拉取最新代码时出错' })
  }
})

// Git restore (reset --hard)
app.post('/api/git/restore', async (req, res) => {
  try {
    const { repoPath } = await readGitConfig()
    if (!repoPath) {
      return res.status(400).json({ success: false, message: '请先配置Git项目路径' })
    }
    const git = simpleGit(repoPath)

    // Get current branch name
    const status = await git.status()
    const currentBranch = status.current

    if (!currentBranch) {
      return res.status(400).json({ success: false, message: '无法获取当前分支信息' })
    }

    // Check if there are any changes to restore
    if (status.files.length === 0) {
      return res.json({ success: true, message: '当前分支没有需要还原的改动', branch: currentBranch })
    }

    // Perform git reset --hard to restore all changes
    await git.reset('hard')
    
    // Broadcast the restore event so other clients can be notified
    broadcastToGitClients({
      type: 'git_event',
      event: 'restore',
      branch: currentBranch,
      message: `分支 ${currentBranch} 的改动已被还原`,
    });

    res.json({ success: true, message: `分支 '${currentBranch}' 的所有改动已成功还原`, branch: currentBranch })
  } catch (error) {
    console.error('Error restoring changes:', error)
    res.status(500).json({ success: false, message: '还原代码时出错: ' + error.message })
  }
})

// Save remark
app.post('/api/git/remark', async (req, res) => {
  const { branch, remark } = req.body
  if (!branch) {
    return res.status(400).json({ success: false, message: '分支名不能为空' })
  }

  try {
    const config = await readGitConfig()
    config.remarks[branch] = remark
    await writeGitConfig(config)

    // Broadcast event for remark update
    broadcastToGitClients({
      type: 'git_event',
      event: 'remark_updated',
      branch: branch,
      remark: remark
    });

    res.json({ success: true, message: '备注已保存' })
  } catch (error) {
    console.error('Error saving remark:', error)
    res.status(500).json({ success: false, message: '保存备注时出错' })
  }
})

// Toggle favorite status
app.post('/api/git/favorite', async (req, res) => {
  const { branch, isFavorite } = req.body;
  if (!branch) {
    return res.status(400).json({ success: false, message: '分支名不能为空' });
  }

  try {
    const config = await readGitConfig();
    const favorites = new Set(config.favoriteBranches);

    if (isFavorite) {
      favorites.add(branch);
    } else {
      favorites.delete(branch);
    }

    config.favoriteBranches = [...favorites];
    await writeGitConfig(config);

    // Broadcast event for specific favorite toggle
    broadcastToGitClients({
      type: 'git_event',
      event: 'favorite_toggled',
      branch: branch,
      isFavorite: isFavorite
    });

    res.json({ success: true, message: '常用分支状态已更新' });
  } catch (error) {
    console.error('Error updating favorite status:', error);
    res.status(500).json({ success: false, message: '更新常用状态时出错' });
  }
});

// Global state for tracking the current deployment
const DEPLOY_PASSWORD = 'blacktom2025'; // Do not expose this

// 设置新部署文件的默认权限
async function setDefaultPermissions(ssh, remotePath, config, broadcast, deployedFiles = []) {
    try {
        const { defaultPerms, owner } = config.sshConfig;
        const targetPerms = defaultPerms || '755';
        const targetOwner = owner || 'www:www';
        
        broadcast(`[PERMS] 设置新部署文件权限（${targetPerms}）和所有者（${targetOwner}）...`);
        
        if (deployedFiles.length > 0) {
            // 只修复指定的部署文件
            for (const file of deployedFiles) {
                const filePath = `${remotePath}/${file}`;
                await ssh.execCommand(`sudo chmod ${targetPerms} '${filePath}' 2>/dev/null || true`);
                await ssh.execCommand(`sudo chown ${targetOwner} '${filePath}' 2>/dev/null || true`);
            }
            broadcast(`[PERMS] ✅ 已设置 ${deployedFiles.length} 个新文件的权限！`);
        } else {
            // 如果没有指定文件列表，则处理所有新文件（仅限maxdepth 1，不影响子目录）
            await ssh.execCommand(`find '${remotePath}' -maxdepth 1 -type f -exec chmod ${targetPerms} {} +`);
            await ssh.execCommand(`find '${remotePath}' -maxdepth 1 -type d -exec chmod ${targetPerms} {} +`);
            await ssh.execCommand(`find '${remotePath}' -maxdepth 1 -exec sudo chown ${targetOwner} {} +`);
            broadcast(`[PERMS] ✅ 新文件权限设置完成！`);
        }
        
        // 时间戳已在文件覆盖后立即更新
        
    } catch (error) {
        broadcast(`[WARN] 设置默认权限时出错: ${error.message}`);
    }
}

// 缓存目标目录的权限信息
async function cacheFilePermissions(ssh, remotePath, broadcast) {
    try {
        broadcast(`[CACHE] 正在缓存现有文件权限...`);
        
        // 先检查文件数量，如果过多则跳过权限缓存
        const countResult = await ssh.execCommand(`find '${remotePath}' -maxdepth 1 | wc -l`, { 
            execOptions: { timeout: 10000 } 
        });
        
        const fileCount = parseInt(countResult.stdout.trim()) || 0;
        broadcast(`[CACHE] 目标目录包含 ${fileCount} 个文件/目录`);
        
        if (fileCount > 1000) {
            broadcast(`[CACHE] 文件数量过多（${fileCount} > 1000），跳过权限缓存以提高性能`);
            broadcast(`[CACHE] 将在部署后设置默认权限`);
            return new Map();
        }
        
        // 获取所有文件和目录的权限、所有者信息（增加超时时间）
        const permResult = await ssh.execCommand(`find '${remotePath}' -maxdepth 1 -printf "%p|%m|%u|%g|%y\\n" 2>/dev/null || true`, { 
            execOptions: { timeout: 60000 } 
        });
        
        const permissionCache = new Map();
        
        if (permResult.stdout) {
            const lines = permResult.stdout.split('\n').filter(line => line.trim());
            broadcast(`[CACHE] 解析 ${lines.length} 行权限信息...`);
            
            for (const line of lines) {
                const [fullPath, perms, user, group, type] = line.split('|');
                if (fullPath && perms) {
                    const relativePath = fullPath.replace(remotePath + '/', '').replace(remotePath, '.');
                    permissionCache.set(relativePath, {
                        perms: perms,
                        user: user,
                        group: group,
                        type: type, // f=file, d=directory, l=link
                        fullPath: fullPath
                    });
                }
            }
        } else {
            broadcast(`[CACHE] 警告：find命令未返回结果，可能目录为空或权限不足`);
        }
        
        broadcast(`[CACHE] ✅ 已缓存 ${permissionCache.size} 个文件/目录的权限信息`);
        return permissionCache;
    } catch (error) {
        broadcast(`[ERROR] 缓存权限信息失败: ${error.message}`);
        if (error.message.includes('timeout') || error.message.includes('TIMEOUT')) {
            broadcast(`[ERROR] 权限缓存超时，这通常是因为目标目录文件过多`);
            broadcast(`[ERROR] 建议：如果是生产环境，考虑精简目录内容或联系管理员优化`);
        }
        broadcast(`[ERROR] 将跳过权限缓存，使用默认权限策略`);
        return new Map();
    }
}

// 根据缓存的权限恢复被覆盖文件的权限
async function restoreFilePermissions(ssh, remotePath, permissionCache, config, broadcast, overwrittenFiles = []) {
    try {
        broadcast(`[RESTORE] 开始恢复被覆盖文件权限...`);
        
        if (!permissionCache || permissionCache.size === 0) {
            broadcast(`[RESTORE] 无权限缓存，使用默认权限设置...`);
            await setDefaultPermissions(ssh, remotePath, config, broadcast, overwrittenFiles);
            return;
        }
        
        let restoredCount = 0;
        const { defaultPerms, owner } = config.sshConfig;
        const defaultPermValue = defaultPerms || '755';
        const defaultOwnerValue = owner || 'www:www';
        
        broadcast(`[RESTORE] 正在恢复 ${permissionCache.size} 个文件的权限...`);
        
        // 简化权限恢复：只恢复实际被覆盖的文件
        if (permissionCache.size > 0) {
            broadcast(`[RESTORE] 采用简化策略恢复权限...`);
            
            // 构建权限恢复命令，只处理存在的文件
            const permCmds = [];
            const ownerCmds = [];
            
            for (const [relativePath, cached] of permissionCache) {
                // 只对部署目录下的直接文件进行权限恢复
                if (!cached.fullPath.includes('..') && cached.fullPath.startsWith(remotePath)) {
                    permCmds.push(`test -e '${cached.fullPath}' && sudo chmod ${cached.perms} '${cached.fullPath}' || true`);
                    ownerCmds.push(`test -e '${cached.fullPath}' && sudo chown ${cached.user}:${cached.group} '${cached.fullPath}' || true`);
                }
            }
            
            if (permCmds.length > 0) {
                // 分批执行，每批最多5个命令
                for (let i = 0; i < permCmds.length; i += 5) {
                    const batch = permCmds.slice(i, i + 5);
                    const batchCmd = batch.join(' ; ');
                    
                    try {
                        await ssh.execCommand(batchCmd, { execOptions: { timeout: 20000 } });
                        restoredCount += batch.length;
                        
                        if (i + 5 < permCmds.length) {
                            broadcast(`[RESTORE] 已处理 ${Math.min(i + 5, permCmds.length)}/${permCmds.length} 个文件的权限...`);
                        }
                    } catch (error) {
                        broadcast(`[RESTORE] 批量权限恢复警告: ${error.message}`);
                    }
                }
                
                // 处理所有者
                for (let i = 0; i < ownerCmds.length; i += 5) {
                    const batch = ownerCmds.slice(i, i + 5);
                    const batchCmd = batch.join(' ; ');
                    
                    try {
                        await ssh.execCommand(batchCmd, { execOptions: { timeout: 20000 } });
                    } catch (error) {
                        broadcast(`[RESTORE] 批量所有者设置警告: ${error.message}`);
                    }
                }
            }
        }
        
        // 处理新文件（设置默认权限）
        const newFiles = overwrittenFiles.filter(file => {
            const relativePath = file.replace(/\/$/, '');
            return !permissionCache.has(relativePath) && !permissionCache.has('./' + relativePath);
        });
        
        if (newFiles.length > 0) {
            broadcast(`[RESTORE] 为 ${newFiles.length} 个新文件设置默认权限...`);
            await setDefaultPermissions(ssh, remotePath, config, broadcast, newFiles);
        }
        
        // 时间戳已在文件覆盖后立即更新
        
        broadcast(`[RESTORE] ✅ 权限恢复完成！已恢复 ${restoredCount} 个文件的权限`);
    } catch (error) {
        broadcast(`[ERROR] 恢复权限时出错: ${error.message}`);
        broadcast(`[ERROR] 错误详情: ${error.stack}`);
        // 不抛出错误，让部署继续进行
    }
}

// 清理临时目录函数
async function cleanupTempDirectory(ssh, remoteTempPath, broadcast) {
    try {
        // 先检查临时目录是否还存在
        const checkResult = await ssh.execCommand(`test -d '${remoteTempPath}' && echo "exists" || echo "not_exists"`, { execOptions: { timeout: 10000 } });
        if (checkResult.stdout.trim() === "exists") {
            broadcast(`[SSH] 开始清理临时目录: ${remoteTempPath}`);
            
            // 多次尝试删除临时目录
            for (let attempt = 1; attempt <= 3; attempt++) {
                try {
                    broadcast(`[SSH] 尝试清理临时目录 (${attempt}/3)...`);
                    
                    // 先尝试标准删除
                    const rmResult = await ssh.execCommand(`sudo rm -rf '${remoteTempPath}'`, { execOptions: { timeout: 30000 } });
                    if (rmResult.code === 0) {
                        broadcast(`[SSH] ✅ 临时目录清理完成。`);
                        return;
                    } else {
                        broadcast(`[WARN] 标准删除失败 (${attempt}/3): ${rmResult.stderr}`);
                    }
                    
                    // 如果标准删除失败，尝试修改权限后删除
                    if (attempt === 1) {
                        broadcast(`[SSH] 尝试修改权限后删除...`);
                        await ssh.execCommand(`sudo chmod -R 777 '${remoteTempPath}' 2>/dev/null || true`, { execOptions: { timeout: 15000 } });
                        await ssh.execCommand(`sudo chown -R root:root '${remoteTempPath}' 2>/dev/null || true`, { execOptions: { timeout: 15000 } });
                    }
                    
                    // 如果还是失败，尝试强制删除
                    if (attempt === 2) {
                        broadcast(`[SSH] 尝试强制删除...`);
                        await ssh.execCommand(`sudo find '${remoteTempPath}' -type f -exec sudo rm -f {} + 2>/dev/null || true`, { execOptions: { timeout: 30000 } });
                        await ssh.execCommand(`sudo find '${remoteTempPath}' -type d -exec sudo rmdir {} + 2>/dev/null || true`, { execOptions: { timeout: 30000 } });
                    }
                    
                    // 最后尝试使用 rm -rf
                    const finalRm = await ssh.execCommand(`sudo rm -rf '${remoteTempPath}' 2>/dev/null || true`, { execOptions: { timeout: 30000 } });
                    if (finalRm.code === 0) {
                        broadcast(`[SSH] ✅ 临时目录强制清理完成。`);
                        return;
                    }
                    
                    // 如果是最后一次尝试仍然失败
                    if (attempt === 3) {
                        // 检查是否还有残留文件
                        const remainingCheck = await ssh.execCommand(`ls -la '${remoteTempPath}' 2>/dev/null || echo "directory_not_found"`);
                        if (remainingCheck.stdout.includes("directory_not_found")) {
                            broadcast(`[SSH] ✅ 临时目录已成功清理。`);
                            return;
                        } else {
                            broadcast(`[ERROR] 无法完全删除临时目录 ${remoteTempPath}，存在残留文件:`);
                            broadcast(`[ERROR] ${remainingCheck.stdout}`);
                            broadcast(`[ERROR] 请手动清理剩余文件。`);
                        }
                    }
                    
                } catch (attemptError) {
                    broadcast(`[WARN] 清理尝试 ${attempt} 失败: ${attemptError.message}`);
                    if (attempt < 3) {
                        await new Promise(resolve => setTimeout(resolve, 2000)); // 等待2秒后重试
                    }
                }
            }
        } else {
            broadcast(`[SSH] 临时目录已不存在，无需清理。`);
        }
    } catch (error) {
        broadcast(`[WARN] 清理临时目录时出错: ${error.message}`);
        // 尝试最后的清理方式
        try {
            broadcast(`[SSH] 尝试最后的清理方式...`);
            await ssh.execCommand(`sudo rm -rf '${remoteTempPath}' 2>/dev/null || true`, { execOptions: { timeout: 30000 } });
            broadcast(`[SSH] 最后清理尝试完成。`);
        } catch (finalError) {
            broadcast(`[ERROR] 最终清理失败: ${finalError.message}`);
        }
    }
}

// 最终部署后处理函数（专注于权限处理）
async function finalizeDeployment(ssh, remotePath, broadcast, permissionCache, config, deployedFiles = []) {
    try {
        broadcast(`[PERMS] 开始权限处理...`);
        
        // 恢复权限
        await restoreFilePermissions(ssh, remotePath, permissionCache, config, broadcast, deployedFiles);
        
        // 显示最终状态
        broadcast(`[FINAL] 检查部署结果...`);
        const statusResult = await ssh.execCommand(`ls -la ${remotePath} | head -10`);
        if (statusResult.stdout) {
            broadcast(`[FINAL] 部署文件列表:\n${statusResult.stdout}`);
        }
        
        broadcast(`[PERMS] ✅ 权限处理完成！`);
    } catch (error) {
        broadcast(`[ERROR] 权限处理出错: ${error.message}`);
        // 权限处理失败不应该影响部署成功
        broadcast(`[WARN] 权限处理失败，但文件已成功部署`);
    }
}



// This function contains the shared upload/compare/resolve logic
async function sshUploadAndDeploy(config, localPath) {
    const { ssh, broadcast } = currentDeployment;
    const { host, port, username, privateKeyPath, remotePath, buildDir } = config.sshConfig;

    // 1. 先创建压缩包
    broadcast(`[ZIP] 正在创建压缩包...`);
    const localZipPath = path.join(__dirname, `_temp_deploy_${Date.now()}.zip`);
    const output = createWriteStream(localZipPath);
    const archive = archiver('zip', { zlib: { level: 9 } });

    archive.on('progress', (progress) => {
        const percent = Math.round((progress.entries.processed / progress.entries.total) * 100);
        broadcastToGitClients({ type: 'deploy_progress', progress: percent, stage: 'zip' });
    });

    archive.pipe(output);
    archive.directory(localPath, false);
    await archive.finalize();
    broadcastToGitClients({ type: 'deploy_progress', progress: 100, stage: 'zip' });
    broadcast(`[ZIP] 压缩完成。`);
    
    // 2. 压缩包成功后才连接SSH，优化连接参数
    const absoluteKeyPath = path.join(__dirname, '..', privateKeyPath);
    const privateKey = await fs.readFile(absoluteKeyPath, 'utf-8');
    
    broadcast(`[SSH] 正在连接到 ${username}@${host}:${port}...`);
    await ssh.connect({ 
        host, 
        port, 
        username, 
        privateKey,
        readyTimeout: 60000,     // 增加到60秒
        timeout: 60000,          // 增加到60秒
        keepaliveInterval: 20000, // 减少到20秒，更频繁的心跳
        keepaliveCountMax: 20,    // 增加最大心跳次数
        algorithms: {
            serverHostKey: ['ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519']
        }
    });
    broadcast(`[SSH] 连接成功。`);
    
    // 3. 上传和解压
    const remoteTempDir = `_temp_${Date.now()}`;
    const remoteTempUploadPath = path.posix.join(remotePath, remoteTempDir);
    const remoteZipPath = path.posix.join(remoteTempUploadPath, 'deploy.zip');
    
    currentDeployment.remoteTempPath = remoteTempUploadPath;

    broadcast(`[SSH] 准备上传压缩包...`);
    broadcast(`[SSH] 目标临时目录: ${remoteTempUploadPath}`);
    
    broadcast(`[SSH] 正在上传 deploy.zip...`);
    // 优化目录创建和权限设置
    await ssh.execCommand(`sudo mkdir -p ${remoteTempUploadPath} && sudo chmod 755 ${remoteTempUploadPath}`);
    await ssh.execCommand(`sudo chown -R ${username}:${username} ${remoteTempUploadPath}`);
    
    await ssh.putFile(localZipPath, remoteZipPath, null, {
        step: (total_transferred, chunk, total) => {
            const percent = Math.round((total_transferred / total) * 100);
            broadcastToGitClients({ type: 'deploy_progress', progress: percent, stage: 'upload' });
        }
    });
    broadcastToGitClients({ type: 'deploy_progress', progress: 100, stage: 'upload' });
    broadcast(`[SSH] 压缩包上传成功。`);
    
    broadcast(`[SSH] 正在远程服务器上解压...`);
    await ssh.execCommand(`unzip -o ${remoteZipPath} -d ${remoteTempUploadPath}`);
    await ssh.execCommand(`rm ${remoteZipPath}`);
    broadcast(`[SSH] 解压完成。`);
    
    // 清理本地压缩包
    await fs.unlink(localZipPath);

    // 4. 比对文件，决定权限处理策略
    broadcast(`[SSH] 正在比对文件...`);
    const tempLsResult = await ssh.execCommand(`ls -Ap ${remoteTempUploadPath}`);
    const destLsResult = await ssh.execCommand(`ls -Ap ${remotePath}`);
    
    const tempFiles = new Set(tempLsResult.stdout.split('\n').filter(Boolean));
    const destFiles = new Set(destLsResult.stdout.split('\n').filter(Boolean));

    const conflicts = [...tempFiles].filter(file => destFiles.has(file)).map(name => ({
        name: name.replace('/', ''),
        type: name.endsWith('/') ? 'dir' : 'file'
    }));
    
    currentDeployment.nonConflicts = [...tempFiles].filter(file => !destFiles.has(file));

    if (conflicts.length > 0) {
        // 需要覆盖：缓存现有权限
        broadcast(`[CACHE] 检测到需要覆盖的文件，正在缓存权限...`);
        const permissionCache = await cacheFilePermissions(ssh, remotePath, broadcast);
        currentDeployment.permissionCache = permissionCache;
        
        broadcast(`[CONFIRM] 发现 ${conflicts.length} 个同名文件/目录，等待用户确认覆盖...`);
        broadcastToGitClients({ type: 'deploy_conflict', files: conflicts });
        broadcast(`[SSH] SSH连接保持中，等待用户操作...`);
        
        // 设置等待用户确认的标志，防止部署进程被提前清理
        currentDeployment.waitingForUserConfirmation = true;
        
        // 启动更频繁的心跳机制，保持SSH连接和部署进程活跃
        const heartbeatInterval = setInterval(async () => {
            try {
                if (ssh.connection && !currentDeployment.cancelled) {
                    await ssh.execCommand('echo "heartbeat"', { execOptions: { timeout: 10000 } });
                    broadcast(`[HEARTBEAT] SSH连接保持活跃 - ${new Date().toLocaleTimeString()}`);
                } else {
                    clearInterval(heartbeatInterval);
                }
            } catch (error) {
                broadcast(`[WARN] 心跳检测失败: ${error.message}`);
                // 心跳失败时不要清除interval，让重连逻辑处理
            }
        }, 15000); // 减少到15秒一次心跳
        
        // 保存心跳定时器到部署对象
        currentDeployment.heartbeatInterval = heartbeatInterval;
        
        // 延长部署超时时间到20分钟
        const deploymentTimeout = setTimeout(() => {
            if (currentDeployment && !currentDeployment.cancelled) {
                broadcast(`[TIMEOUT] 部署等待超时（20分钟），自动清理部署进程`);
                try {
                    if (currentDeployment.heartbeatInterval) {
                        clearInterval(currentDeployment.heartbeatInterval);
                    }
                    if (currentDeployment.ssh && currentDeployment.ssh.connection) {
                        // 尝试清理临时目录
                        currentDeployment.ssh.execCommand(`sudo rm -rf '${remoteTempUploadPath}' 2>/dev/null || true`);
                        currentDeployment.ssh.dispose();
                    }
                    // 清除等待用户确认的标志
                    currentDeployment.waitingForUserConfirmation = false;
                } catch (error) {
                    console.error('清理超时部署时出错:', error);
                }
                currentDeployment = null;
            }
        }, 20 * 60 * 1000); // 延长到20分钟
        
        currentDeployment.deploymentTimeout = deploymentTimeout;
    } else {
        // 无需覆盖：直接部署并设置默认权限
        broadcast(`[SSH] 没有发现同名文件，直接部署...`);
        
        // 使用rsync同步所有新文件（不保持原时间戳）
        const rsyncResult = await ssh.execCommand(`sudo rsync -rlpgov '${remoteTempUploadPath}/' '${remotePath}/' --exclude='${remoteTempUploadPath.split('/').pop()}'`);
        
        if (rsyncResult.code === 0) {
            broadcast(`[SSH] ✅ 文件同步成功`);
        } else {
            broadcast(`[WARN] rsync同步失败，使用传统方式...`);
            await ssh.execCommand(`sudo mv ${remoteTempUploadPath}/* ${remotePath}/ 2>/dev/null || true`);
        }
        
        // 立即更新新部署文件的时间戳
        broadcast(`[SSH] 更新新部署文件的时间戳...`);
        for (const file of currentDeployment.nonConflicts) {
            if (!file.includes(' ') && !file.includes('..')) {
                await ssh.execCommand(`sudo touch '${remotePath}/${file}' 2>/dev/null || true`);
            }
        }
        
        // 立即清理临时目录
        broadcast(`[SSH] 正在清理临时目录...`);
        await cleanupTempDirectory(ssh, remoteTempUploadPath, broadcast);
        
        // 设置新文件的默认权限
        await setDefaultPermissions(ssh, remotePath, config, broadcast, currentDeployment.nonConflicts);
        
        broadcast(`[DEPLOY] 部署成功完成！`);
        // 不在这里调用end()，让runDeploy的finally块处理
    }
}

// Build and Deploy
app.post('/api/git/deploy', async (req, res) => {
    const { branch, password } = req.body;
    if (!branch) {
        return res.status(400).json({ success: false, message: '需要提供分支名称' });
    }

    if (password !== DEPLOY_PASSWORD) {
        return res.status(401).json({ success: false, message: '校验密码错误，无权执行此操作。' });
    }
    
    if (currentDeployment) {
        return res.status(409).json({ success: false, message: `部署已在进行中: ${currentDeployment.branch}` });
    }

    const broadcast = (log) => {
        broadcastToGitClients({ type: 'deploy_log', log });
        console.log(log);
    };

    try {
        const config = await readGitConfig();
        if (branch !== config.productionBranch) {
            return res.status(403).json({ success: false, message: `分支 ${branch} 不是指定的生产分支。` });
        }
        
        if (!config.repoPath) {
            return res.status(400).json({ success: false, message: '请先配置Git项目路径' });
        }

        const git = simpleGit(config.repoPath);
        const status = await git.status();
        if (status.current !== branch) {
            return res.status(400).json({ success: false, message: `部署失败：当前项目分支为 '${status.current}'，请先切换到生产分支 '${branch}' 再执行部署操作。` });
        }

        const { host, port, username, privateKeyPath, remotePath } = config.sshConfig;
        if (!host || !username || !privateKeyPath || !remotePath) {
            return res.status(400).json({ success: false, message: 'SSH配置不完整，请先设置主机、用户名、私钥路径和远程路径。' });
        }
        
        res.json({ success: true, message: `分支 ${branch} 的构建和部署流程已开始...` });

        // --- Run deployment asynchronously ---
        const runDeploy = async () => {
            currentDeployment = new Deployment(branch, broadcast);

            try {
                broadcast(`[DEPLOY] 流程开始，目标分支: ${branch}`);
                
                // 1. 拉取最新代码
                broadcast(`[GIT] 正在拉取 ${branch} 分支的最新代码...`);
                await git.pull('origin', branch);
                if (currentDeployment.cancelled) throw new Error("部署已被用户取消。");
                broadcast(`[GIT] 拉取完成。`);

                // 2. 执行构建
                broadcast(`[NPM] 正在执行构建 (npm run build)...`);
                const localBuildPath = path.join(config.repoPath, config.sshConfig.buildDir || 'dist');
                await new Promise((resolve, reject) => {
                    const buildProcess = spawn('npm', ['run', 'build'], { cwd: config.repoPath, shell: true });
                    currentDeployment.childProcess = buildProcess;
                    
                    buildProcess.stdout.on('data', (data) => broadcast(`[BUILD] > ${data.toString().trim()}`));
                    buildProcess.stderr.on('data', (data) => broadcast(`[BUILD_ERROR] > ${data.toString().trim()}`));

                    buildProcess.on('close', (code) => {
                        if (currentDeployment.cancelled) {
                            return reject(new Error("构建过程被用户取消。"));
                        }
                        if (code === 0) {
                            broadcast(`[NPM] 构建成功完成。`);
                            resolve();
                        } else {
                            reject(new Error(`构建失败，退出码: ${code}`));
                        }
                    });
                    buildProcess.on('error', (err) => reject(new Error(`无法启动构建进程: ${err.message}`)));
                });
                if (currentDeployment.cancelled) return;
                
                await sshUploadAndDeploy(config, localBuildPath);

            } catch (error) {
                console.error('部署过程中发生错误:', error);
                if (!error.message.includes('取消')) {
                    broadcast(`[DEPLOY] 部署失败: ${error.message}`);
                } else {
                     broadcast(`[DEPLOY] ${error.message}`);
                }
                // 出错时清除等待确认标志
                if (currentDeployment) {
                    currentDeployment.waitingForUserConfirmation = false;
                }
            } finally {
                // 只有在部署完成或出错时才清理，等待用户确认时不清理
                if (currentDeployment && !currentDeployment.waitingForUserConfirmation) {
                    broadcast(`[DEPLOY] 清理部署进程资源...`);
                    currentDeployment = null;
                }
            }
        }
        runDeploy();

    } catch (error) {
        console.error('部署准备过程中发生错误:', error);
        broadcast(`[DEPLOY] 部署准备失败: ${error.message}`);
    }
});

app.post('/api/git/deploy/cancel', (req, res) => {
    const { branch } = req.body;
    if (currentDeployment && currentDeployment.branch === branch) {
        currentDeployment.cancelled = true;
        currentDeployment.waitingForUserConfirmation = false; // 清除等待确认标志
        
        const { childProcess, ssh, broadcast } = currentDeployment;

        if (childProcess) {
            childProcess.kill('SIGTERM'); // Send termination signal
            broadcast(`[CANCEL] 取消信号已发送至构建进程...`);
        }

        if (ssh && ssh.connection) {
            ssh.dispose();
            broadcast(`[CANCEL] SSH连接正在关闭...`);
        }

        res.json({ success: true, message: '取消请求已发送。' });
    } else {
        res.status(404).json({ success: false, message: '没有找到正在进行的部署任务。' });
    }
});

// 获取当前部署状态
app.get('/api/git/deploy/status', (req, res) => {
    console.log(`[STATUS] 检查部署状态，currentDeployment: ${currentDeployment ? 'exists' : 'null'}`);
    if (currentDeployment) {
        const status = {
            success: true,
            isDeploying: true,
            branch: currentDeployment.branch,
            hasHeartbeat: !!currentDeployment.heartbeatInterval,
            hasTimeout: !!currentDeployment.deploymentTimeout,
            cancelled: currentDeployment.cancelled,
            hasSSH: !!(currentDeployment.ssh && currentDeployment.ssh.connection),
            remoteTempPath: currentDeployment.remoteTempPath || 'unknown',
            waitingForUserConfirmation: currentDeployment.waitingForUserConfirmation
        };
        console.log(`[STATUS] 部署状态:`, status);
        res.json(status);
    } else {
        console.log(`[STATUS] 无部署任务`);
        res.json({
            success: true,
            isDeploying: false
        });
    }
});

app.post('/api/git/deploy/resolve', async (req, res) => {
    const { overwriteItems = [], cancel = false } = req.body;

    console.log(`[RESOLVE] 接收到resolve请求: overwriteItems=${overwriteItems.length}, cancel=${cancel}`);
    
    if (!currentDeployment) {
        console.error('resolve接口被调用但没有找到部署任务');
        console.error('可能的原因：1. 部署超时 2. 连接中断 3. 部署被取消 4. 服务器重启');
        return res.status(404).json({ 
            success: false, 
            message: '没有找到正在进行的部署任务，可能已超时、连接中断或部署被取消。请重新开始部署。' 
        });
    }
    
    console.log(`[RESOLVE] 当前部署任务: ${currentDeployment.branch}, cancelled=${currentDeployment.cancelled}`);
    
    // 清除等待用户确认的标志
    currentDeployment.waitingForUserConfirmation = false;
    
    let { ssh, broadcast, nonConflicts, remoteTempPath, heartbeatInterval, deploymentTimeout } = currentDeployment;
    
    // 清理心跳定时器和超时定时器
    if (heartbeatInterval) {
        clearInterval(heartbeatInterval);
        currentDeployment.heartbeatInterval = null;
        broadcast(`[SSH] 停止心跳检测，开始处理用户操作`);
    }
    
    if (deploymentTimeout) {
        clearTimeout(deploymentTimeout);
        currentDeployment.deploymentTimeout = null;
        broadcast(`[SSH] 清除部署超时定时器`);
    }
    
    let config, activeSsh;
    
    try {
        broadcast(`[RESOLVE] 开始处理用户操作...`);
        console.log(`[RESOLVE] 读取Git配置...`);
        config = await readGitConfig();
        console.log(`[RESOLVE] 配置读取成功`);
        
        const { host, port, username, privateKeyPath, remotePath } = config.sshConfig;
        if (!host || !port || !username || !privateKeyPath || !remotePath) {
            throw new Error(`SSH配置不完整: host=${host}, port=${port}, username=${username}, privateKeyPath=${privateKeyPath}, remotePath=${remotePath}`);
        }
        
        console.log(`[RESOLVE] SSH配置验证通过: ${username}@${host}:${port}`);
        activeSsh = ssh; // Track the active SSH connection
        
        // 确保SSH连接可用，如果不可用则重连
        broadcast(`[SSH] 检查SSH连接状态...`);
        console.log(`[RESOLVE] 开始SSH连接检查...`);
        let connectionValid = false;
        
        // 尝试5次连接检查/重连，增加重连次数
        for (let attempt = 1; attempt <= 5; attempt++) {
            try {
                // 检查当前连接是否有效
                if (activeSsh && activeSsh.connection) {
                    await activeSsh.execCommand('echo "connection_test"', { execOptions: { timeout: 10000 } });
                    connectionValid = true;
                    broadcast(`[SSH] 连接状态正常。`);
                    break;
                } else {
                    throw new Error('SSH连接对象无效');
                }
            } catch (e) {
                console.error(`[RESOLVE] SSH连接检查失败 (尝试 ${attempt}/5):`, e);
                broadcast(`[SSH] 连接检查失败 (尝试 ${attempt}/5): ${e.message}`);
                
                if (attempt < 5) {
                    broadcast(`[SSH] 正在尝试重新连接...`);
                    console.log(`[RESOLVE] 开始重连尝试 ${attempt}/5`);
                    try {
                        // 释放旧连接
                        if (activeSsh && activeSsh.connection) {
                            console.log(`[RESOLVE] 释放旧的SSH连接...`);
                            activeSsh.dispose();
                        }
                        
                        // 创建新连接，使用与初始连接相同的优化参数
                        console.log(`[RESOLVE] 创建新的SSH连接...`);
                        activeSsh = new NodeSSH();
                        const privateKey = await fs.readFile(path.join(__dirname, '..', privateKeyPath), 'utf-8');
                        console.log(`[RESOLVE] 私钥读取成功，开始连接 ${username}@${host}:${port}`);
                        await activeSsh.connect({ 
                            host, 
                            port, 
                            username, 
                            privateKey,
                            readyTimeout: 60000,
                            timeout: 60000,
                            keepaliveInterval: 20000,
                            keepaliveCountMax: 20,
                            algorithms: {
                                serverHostKey: ['ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519']
                            }
                        });
                        
                        // 更新部署对象中的SSH连接
                        currentDeployment.ssh = activeSsh;
                        console.log(`[RESOLVE] SSH重连成功 (尝试 ${attempt}/5)`);
                        broadcast(`[SSH] 重连成功 (尝试 ${attempt}/5)。`);
                        
                        // 验证重连后的连接
                        console.log(`[RESOLVE] 验证重连后的连接...`);
                        await activeSsh.execCommand('echo "reconnection_test"', { execOptions: { timeout: 10000 } });
                        connectionValid = true;
                        console.log(`[RESOLVE] 连接验证成功`);
                        break;
                    } catch (reconnectError) {
                        console.error(`[RESOLVE] 重连失败 (尝试 ${attempt}/5):`, reconnectError);
                        broadcast(`[SSH] 重连失败 (尝试 ${attempt}/5): ${reconnectError.message}`);
                        if (attempt === 5) {
                            throw new Error(`SSH连接失败，已尝试5次重连：${reconnectError.message}`);
                        }
                        // 逐渐增加等待时间：2秒、4秒、6秒、8秒
                        const waitTime = attempt * 2000;
                        console.log(`[RESOLVE] 等待 ${waitTime/1000} 秒后重试...`);
                        broadcast(`[SSH] 等待 ${waitTime/1000} 秒后重试...`);
                        await new Promise(resolve => setTimeout(resolve, waitTime));
                    }
                } else {
                    throw new Error(`SSH连接失败，已尝试5次重连：${e.message}`);
                }
            }
        }

        if (!connectionValid) {
            throw new Error('无法建立有效的SSH连接');
        }

        if (cancel) {
            console.log(`[RESOLVE] 用户选择取消部署`);
            broadcast(`[SSH] 用户已取消，正在清理临时文件...`);
            try {
                await cleanupTempDirectory(activeSsh, remoteTempPath, broadcast);
                console.log(`[RESOLVE] 临时文件清理完成`);
            } catch (cleanupError) {
                console.error(`[RESOLVE] 清理临时文件失败:`, cleanupError);
                broadcast(`[WARN] 清理临时文件失败: ${cleanupError.message}`);
            }
            broadcast(`[DEPLOY] 部署已取消。`);
            currentDeployment.end();
            console.log(`[RESOLVE] 部署取消完成`);
            return res.json({ success: true, message: '部署已取消' });
        }

        if (overwriteItems.length > 0) {
            console.log(`[RESOLVE] 开始处理覆盖文件，数量: ${overwriteItems.length}`);
            broadcast(`[SSH] 正在覆盖选中的 ${overwriteItems.length} 个文件/目录...`);
            
            // 先检查临时目录中的文件是否存在
            console.log(`[RESOLVE] 检查临时目录: ${remoteTempPath}`);
            const tempCheckResult = await activeSsh.execCommand(`ls -la '${remoteTempPath}/'`);
            if (tempCheckResult.code !== 0) {
                console.error(`[RESOLVE] 临时目录访问失败: ${tempCheckResult.stderr}`);
                throw new Error(`临时目录访问失败: ${tempCheckResult.stderr}`);
            }
            console.log(`[RESOLVE] 临时目录检查通过`);
            broadcast(`[SSH] 临时目录文件列表确认完成`);
            
            // 批量覆盖文件，使用 -rlpgo 而不是 -av (去掉时间戳保持)
            const validItems = overwriteItems.filter(item => !item.includes(' ') && !item.includes('..') && item.length > 0);
            broadcast(`[SSH] 有效覆盖文件数量: ${validItems.length}`);
            
            if (validItems.length > 0) {
                // 分批处理，每批最多5个文件
                const batchSize = 5;
                for (let i = 0; i < validItems.length; i += batchSize) {
                    const batch = validItems.slice(i, i + batchSize);
                    const overwritePaths = batch.map(item => `'${remoteTempPath}/${item.replace(/'/g, "'\\''")}'`).join(' ');
                    
                    try {
                        const batchRsync = await activeSsh.execCommand(`sudo rsync -rlpgov ${overwritePaths} '${remotePath}/'`, { execOptions: { timeout: 30000 } });
                        if (batchRsync.code === 0) {
                            broadcast(`[SSH] ✅ 批量覆盖文件成功 (${i + 1}-${Math.min(i + batchSize, validItems.length)}/${validItems.length})`);
                        } else {
                            broadcast(`[WARN] 批量覆盖失败，尝试逐个处理: ${batchRsync.stderr}`);
                            // 逐个处理失败的文件
                            for (const item of batch) {
                                try {
                                    await activeSsh.execCommand(`sudo cp -rf '${remoteTempPath}/${item}' '${remotePath}/' 2>/dev/null || true`, { execOptions: { timeout: 15000 } });
                                    broadcast(`[SSH] 单独处理文件: ${item}`);
                                } catch (individualError) {
                                    broadcast(`[ERROR] 无法处理文件 ${item}: ${individualError.message}`);
                                }
                            }
                        }
                    } catch (batchError) {
                        broadcast(`[ERROR] 批量处理失败: ${batchError.message}`);
                        // 如果批量失败，尝试逐个处理
                        for (const item of batch) {
                            try {
                                await activeSsh.execCommand(`sudo cp -rf '${remoteTempPath}/${item}' '${remotePath}/' 2>/dev/null || true`, { execOptions: { timeout: 15000 } });
                                broadcast(`[SSH] 单独处理文件: ${item}`);
                            } catch (individualError) {
                                broadcast(`[ERROR] 无法处理文件 ${item}: ${individualError.message}`);
                            }
                        }
                    }
                }
            }
            
            // 立即更新覆盖文件的时间戳
            broadcast(`[SSH] 更新覆盖文件的时间戳...`);
            for (const item of validItems) {
                try {
                    await activeSsh.execCommand(`sudo touch '${remotePath}/${item}' 2>/dev/null || true`, { execOptions: { timeout: 5000 } });
                } catch (touchError) {
                    broadcast(`[WARN] 无法更新文件时间戳 ${item}: ${touchError.message}`);
                }
            }
        }

        if (nonConflicts.length > 0) {
            console.log(`[RESOLVE] 开始处理新文件，数量: ${nonConflicts.length}`);
            broadcast(`[SSH] 正在移动 ${nonConflicts.length} 个新文件/目录...`);
            // 批量移动新文件，使用 -rlpgo 而不是 -av (去掉时间戳保持)
            const newPaths = nonConflicts.filter(item => !item.includes(' ') && !item.includes('..')).map(item => `'${remoteTempPath}/${item.replace(/'/g, "'\\''")}'`).join(' ');
            if (newPaths) {
                console.log(`[RESOLVE] 执行批量移动命令: sudo rsync -rlpgov ${newPaths} '${remotePath}/'`);
                const batchMove = await activeSsh.execCommand(`sudo rsync -rlpgov ${newPaths} '${remotePath}/'`);
                if (batchMove.code === 0) {
                    console.log(`[RESOLVE] 批量移动新文件成功`);
                    broadcast(`[SSH] ✅ 批量移动新文件成功`);
                } else {
                    console.error(`[RESOLVE] 批量移动失败: ${batchMove.stderr}`);
                    broadcast(`[WARN] 批量移动失败: ${batchMove.stderr}`);
                }
            }
            
            // 立即更新新文件的时间戳
            broadcast(`[SSH] 更新新文件的时间戳...`);
            for (const item of nonConflicts) {
                if (!item.includes(' ') && !item.includes('..')) {
                    await activeSsh.execCommand(`sudo touch '${remotePath}/${item}' 2>/dev/null || true`);
                }
            }
        }

        // 确保移动所有剩余文件（包括隐藏文件）
        broadcast(`[SSH] 检查并移动剩余文件...`);
        const remainingResult = await activeSsh.execCommand(`find '${remoteTempPath}' -mindepth 1 -maxdepth 1`);
        if (remainingResult.code === 0 && remainingResult.stdout.trim()) {
            broadcast(`[SSH] 发现剩余文件，正在同步...`);
            const rsyncRemaining = await activeSsh.execCommand(`sudo rsync -rlpgov '${remoteTempPath}/' '${remotePath}/' --exclude='${remoteTempPath.split('/').pop()}'`);
            if (rsyncRemaining.code === 0) {
                broadcast(`[SSH] ✅ 剩余文件同步完成`);
            } else {
                broadcast(`[WARN] 同步剩余文件时出现警告: ${rsyncRemaining.stderr}`);
            }
            
            // 更新剩余文件的时间戳
            broadcast(`[SSH] 更新剩余文件的时间戳...`);
            await activeSsh.execCommand(`find '${remotePath}' -maxdepth 1 -newer '${remoteTempPath}' -exec sudo touch {} + 2>/dev/null || true`);
        }
        
        // 立即清理临时目录
        broadcast(`[SSH] 正在清理临时目录...`);
        await cleanupTempDirectory(activeSsh, remoteTempPath, broadcast);
        
        // 最终部署后处理（权限设置）
        console.log(`[RESOLVE] 开始最终部署后处理...`);
        const allDeployedFiles = [...overwriteItems, ...nonConflicts];
        console.log(`[RESOLVE] 总共部署文件数: ${allDeployedFiles.length}`);
        await finalizeDeployment(activeSsh, remotePath, broadcast, currentDeployment.permissionCache, config, allDeployedFiles);
        console.log(`[RESOLVE] 最终部署后处理完成`);
        broadcast(`[DEPLOY] 部署成功完成！`);
        console.log(`[RESOLVE] 部署成功，发送响应`);
        res.json({ success: true, message: '部署已完成' });

    } catch (error) {
        console.error('[RESOLVE] 部署过程中发生错误:', error);
        broadcast(`[DEPLOY] 完成部署时出错: ${error.message}`);
        broadcast(`[ERROR] 详细错误信息: ${error.stack}`);
        
        // 出错时也要清理临时目录
        try {
            if (activeSsh && activeSsh.connection && remoteTempPath) {
                broadcast(`[SSH] 出错清理：正在删除临时目录...`);
                await cleanupTempDirectory(activeSsh, remoteTempPath, broadcast);
            }
        } catch (cleanupError) {
            console.error('[RESOLVE] 清理临时目录失败:', cleanupError);
            broadcast(`[WARN] 出错清理临时目录失败: ${cleanupError.message}`);
        }
        
        // 确保响应被发送
        if (!res.headersSent) {
            res.status(500).json({ success: false, message: `完成部署时出错: ${error.message}` });
        }
    } finally {
        // 结束部署
        console.log('[RESOLVE] 清理部署进程...');
        try {
            if (currentDeployment) {
                currentDeployment.end();
                currentDeployment = null;
                broadcast(`[DEPLOY] 部署进程已清理`);
            }
        } catch (endError) {
            console.error('[RESOLVE] 清理部署进程失败:', endError);
        }
    }
});



app.post('/api/git/ssh-test', async (req, res) => {
    if (currentDeployment) {
        return res.status(409).json({ success: false, message: `部署正在进行中，请稍后再试。` });
    }

    const broadcast = (log) => {
        broadcastToGitClients({ type: 'deploy_log', log });
        console.log(log);
    };
    
    currentDeployment = new Deployment('ssh-test', broadcast);

    try {
        const config = await readGitConfig();
        if (!config.repoPath) {
            throw new Error('请先配置Git项目路径，测试需要从此路径读取/dist目录。');
        }
        const localDistPath = path.join(config.repoPath, 'dist');
        await fs.access(localDistPath);

        res.json({ success: true, message: 'SSH连接测试已开始...' });
        
        broadcast(`[SSH_TEST] 开始SSH连接测试...`);
        await sshUploadAndDeploy(config, localDistPath);
    
    } catch (error) {
        const message = error.code === 'ENOENT'
            ? `测试失败: 在目标项目路径中未找到 'dist' 目录。`
            : error.message;
        broadcast(`[SSH_TEST] ❌ 测试失败: ${message}`);
        if(currentDeployment) currentDeployment.end();
    }
});

// 添加诊断接口
app.post('/api/git/diagnose', async (req, res) => {
    const broadcast = (log) => {
        broadcastToGitClients({ type: 'deploy_log', log });
        console.log(log);
    };
    
    try {
        broadcast(`[DIAGNOSE] 开始诊断SSH连接和权限...`);
        
        const config = await readGitConfig();
        const { host, port, username, privateKeyPath, remotePath } = config.sshConfig;
        
        if (!host || !port || !username || !privateKeyPath || !remotePath) {
            throw new Error('SSH配置不完整');
        }
        
        // 创建SSH连接
        const ssh = new NodeSSH();
        const absoluteKeyPath = path.join(__dirname, '..', privateKeyPath);
        const privateKey = await fs.readFile(absoluteKeyPath, 'utf-8');
        
        broadcast(`[DIAGNOSE] 正在连接到 ${username}@${host}:${port}...`);
        
        await ssh.connect({
            host,
            port,
            username,
            privateKey,
            readyTimeout: 30000,
            timeout: 30000
        });
        
        broadcast(`[DIAGNOSE] SSH连接成功`);
        
        // 测试基本命令
        const whoamiResult = await ssh.execCommand('whoami');
        broadcast(`[DIAGNOSE] 当前用户: ${whoamiResult.stdout}`);
        
        // 测试sudo权限
        const sudoResult = await ssh.execCommand('sudo -n true');
        if (sudoResult.code === 0) {
            broadcast(`[DIAGNOSE] ✅ sudo权限正常`);
        } else {
            broadcast(`[DIAGNOSE] ❌ sudo权限异常: ${sudoResult.stderr}`);
        }
        
        // 测试远程目录
        const pathResult = await ssh.execCommand(`ls -la ${remotePath}`);
        if (pathResult.code === 0) {
            broadcast(`[DIAGNOSE] ✅ 远程目录可访问`);
            broadcast(`[DIAGNOSE] 目录内容:\n${pathResult.stdout}`);
        } else {
            broadcast(`[DIAGNOSE] ❌ 远程目录访问失败: ${pathResult.stderr}`);
        }
        
        // 测试创建临时目录
        const tempDir = `_diagnose_${Date.now()}`;
        const tempPath = path.posix.join(remotePath, tempDir);
        
        const mkdirResult = await ssh.execCommand(`sudo mkdir -p ${tempPath}`);
        if (mkdirResult.code === 0) {
            broadcast(`[DIAGNOSE] ✅ 临时目录创建成功`);
            
            // 测试权限设置
            const chmodResult = await ssh.execCommand(`sudo chmod 755 ${tempPath}`);
            const chownResult = await ssh.execCommand(`sudo chown ${username}:${username} ${tempPath}`);
            
            if (chmodResult.code === 0 && chownResult.code === 0) {
                broadcast(`[DIAGNOSE] ✅ 权限设置成功`);
            } else {
                broadcast(`[DIAGNOSE] ❌ 权限设置失败`);
            }
            
            // 清理临时目录
            await ssh.execCommand(`sudo rm -rf ${tempPath}`);
            broadcast(`[DIAGNOSE] 临时目录已清理`);
        } else {
            broadcast(`[DIAGNOSE] ❌ 临时目录创建失败: ${mkdirResult.stderr}`);
        }
        
        // 测试rsync命令
        const rsyncResult = await ssh.execCommand('which rsync');
        if (rsyncResult.code === 0) {
            broadcast(`[DIAGNOSE] ✅ rsync命令可用: ${rsyncResult.stdout}`);
        } else {
            broadcast(`[DIAGNOSE] ❌ rsync命令不可用，建议安装rsync`);
        }
        
        ssh.dispose();
        broadcast(`[DIAGNOSE] 诊断完成`);
        
        res.json({ success: true, message: '诊断完成' });
        
    } catch (error) {
        console.error('诊断失败:', error);
        broadcast(`[DIAGNOSE] 诊断失败: ${error.message}`);
        res.status(500).json({ success: false, message: `诊断失败: ${error.message}` });
    }
});

const server = app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`)
})

const wss = new WebSocketServer({ server })

// Keep track of clients on the Git page
const gitClients = new Map();

// Function to broadcast the current list of Git users
function broadcastGitUsers() {
    const users = Array.from(gitClients.values());
    const message = JSON.stringify({ type: 'git_users_update', users });

    for (const client of gitClients.keys()) {
        if (client.readyState === WebSocket.OPEN) {
            client.send(message);
        }
    }
}

// Function to broadcast a message to all Git clients
function broadcastToGitClients(message) {
    const serializedMessage = JSON.stringify(message);
    for (const client of gitClients.keys()) {
        if (client.readyState === WebSocket.OPEN) {
            client.send(serializedMessage);
        }
    }
}

// Function to broadcast sync logs to all translation editor clients
function broadcastSyncLog(log) {
    const message = JSON.stringify({ type: 'sync_log', log });
    wss.clients.forEach(client => {
        if (client.readyState === WebSocket.OPEN && !client.isGitClient) {
            client.send(message);
        }
    });
}

wss.on('connection', (ws, req) => {
  const ip = (req.headers['x-forwarded-for'] || req.socket.remoteAddress).replace(/^::ffff:/, '');
  console.log(`Client connected from ${ip}`);

  ws.on('message', message => {
    try {
      const parsedMessage = JSON.parse(message.toString());

      if (parsedMessage.type === 'page_identify' && parsedMessage.page === 'git') {
        ws.isGitClient = true;
        gitClients.set(ws, { ip: ip, connectedAt: Date.now() });
        broadcastGitUsers();
        return;
      }

      // Default behavior for other messages (e.g., from translation editor)
      wss.clients.forEach(client => {
        if (client !== ws && client.readyState === WebSocket.OPEN && !client.isGitClient) {
          client.send(JSON.stringify(parsedMessage));
        }
      });
    } catch (error) {
        // It might be a simple string message. If so, just broadcast it.
        // This is a fallback for simple message passing if JSON parsing fails.
        wss.clients.forEach(client => {
          if (client !== ws && client.readyState === WebSocket.OPEN && !client.isGitClient) {
            client.send(message.toString());
          }
        });
    }
  });

  ws.on('close', () => {
    console.log(`Client disconnected from ${ip}`);
    if (ws.isGitClient) {
      gitClients.delete(ws);
      broadcastGitUsers();
    }
  });

  ws.on('error', (error) => {
    console.error('WebSocket error:', error);
  });
});

// 包管理器检测和安装API
app.get('/api/git/package-managers', async (req, res) => {
    try {
        const config = await readGitConfig();
        const repoPath = config.repoPath;
        
        if (!repoPath) {
            return res.status(400).json({ success: false, message: '项目路径未配置' });
        }

        // 检测包管理器是否全局安装
        const checkPackageManager = (command) => {
            return new Promise((resolve) => {
                const process = spawn(command, ['--version'], { 
                    stdio: 'ignore',
                    cwd: repoPath,
                    env: process.env,
                    shell: true
                });
                process.on('close', (code) => {
                    resolve(code === 0);
                });
                process.on('error', () => {
                    resolve(false);
                });
            });
        };

        const [npmAvailable, pnpmAvailable, yarnAvailable] = await Promise.all([
            checkPackageManager('npm'),
            checkPackageManager('pnpm'),
            checkPackageManager('yarn')
        ]);

        res.json({
            success: true,
            data: {
                npm: npmAvailable,
                pnpm: pnpmAvailable,
                yarn: yarnAvailable
            }
        });
    } catch (error) {
        res.status(500).json({ success: false, message: error.message });
    }
});

// 依赖安装状态管理
let currentDepsInstallation = null;

app.post('/api/git/install-deps', async (req, res) => {
    const { packageManager } = req.body;
    
    if (!packageManager || !['npm', 'pnpm', 'yarn'].includes(packageManager)) {
        return res.status(400).json({ success: false, message: '无效的包管理器' });
    }

    if (currentDepsInstallation && !currentDepsInstallation.ended) {
        return res.status(409).json({ success: false, message: '已有依赖安装任务在进行中' });
    }

    try {
        const config = await readGitConfig();
        const repoPath = config.repoPath;
        
        if (!repoPath) {
            return res.status(400).json({ success: false, message: '项目路径未配置' });
        }

        // 检查项目目录是否存在package.json
        const packageJsonPath = path.join(repoPath, 'package.json');
        try {
            await fs.access(packageJsonPath);
        } catch {
            return res.status(400).json({ success: false, message: '项目目录中未找到package.json文件' });
        }

        res.json({ success: true, message: '依赖安装已开始...' });
        
        // 启动安装过程
        installDependencies(packageManager, repoPath);
        
    } catch (error) {
        res.status(500).json({ success: false, message: error.message });
    }
});

app.post('/api/git/install-deps/cancel', (req, res) => {
    if (currentDepsInstallation && !currentDepsInstallation.ended) {
        currentDepsInstallation.kill('SIGTERM');
        broadcastToGitClients({ type: 'deps_log', log: '[DEPS] ❌ 依赖安装已被用户取消' });
        res.json({ success: true, message: '取消安装请求已发送' });
    } else {
        res.json({ success: false, message: '没有正在进行的依赖安装任务' });
    }
});

// 依赖安装函数
async function installDependencies(packageManager, repoPath) {
    const broadcast = (log) => {
        broadcastToGitClients({ type: 'deps_log', log });
        console.log(log);
    };

    const broadcastProgress = (progress) => {
        broadcastToGitClients({ type: 'deps_progress', progress });
    };

    async function tryInstallDependencies(isRetry = false) {
        const installCommand = packageManager;
        const installArgs = ['install'];
        
        if (!isRetry) {
            broadcast(`[DEPS] 开始使用 ${packageManager} 安装依赖...`);
            broadcast(`[DEPS] 项目路径: ${repoPath}`);
        } else {
            broadcast(`[DEPS] 重新尝试安装依赖...`);
        }
        
        // 添加环境调试信息
        broadcast(`[DEPS] 系统平台: ${process.platform}`);
        broadcast(`[DEPS] Node.js版本: ${process.version}`);
        broadcast(`[DEPS] 工作目录: ${repoPath}`);
        
        broadcast(`[DEPS] 执行命令: ${installCommand} ${installArgs.join(' ')}`);
        broadcastProgress(isRetry ? 50 : 10);
        
        currentDepsInstallation = spawn(installCommand, installArgs, {
            cwd: repoPath,
            stdio: 'pipe',
            env: process.env,
            shell: true // 在Windows上很重要，确保可以找到命令
        });
        
        currentDepsInstallation.stdout.on('data', (data) => {
            const output = data.toString();
            broadcast(`[DEPS] ${output.trim()}`);
            
            // 简单的进度估算
            if (output.includes('downloading') || output.includes('extracting')) {
                broadcastProgress(Math.min(90, Math.random() * 30 + (isRetry ? 50 : 30)));
            }
        });
        
        currentDepsInstallation.stderr.on('data', (data) => {
            const output = data.toString();
            // 过滤掉一些正常的警告信息
            if (!output.includes('WARN') && !output.includes('deprecated')) {
                broadcast(`[DEPS] ${output.trim()}`);
            }
        });
        
        return new Promise((resolve, reject) => {
            currentDepsInstallation.on('close', (code) => {
                currentDepsInstallation.ended = true;
                if (code === 0) {
                    broadcast(`[DEPS] ✅ 依赖安装完成`);
                    broadcastProgress(100);
                    resolve();
                } else {
                    broadcast(`[DEPS] ❌ ${packageManager} 安装失败，退出代码: ${code}`);
                    reject(new Error(`安装失败，退出代码: ${code}`));
                }
            });
            
            currentDepsInstallation.on('error', (error) => {
                currentDepsInstallation.ended = true;
                if (error.code === 'ENOENT') {
                    broadcast(`[DEPS] ❌ ${packageManager} 命令不存在`);
                    reject(new Error(`${packageManager} 命令不存在`));
                } else {
                    broadcast(`[DEPS] ❌ ${packageManager} 执行出错: ${error.message}`);
                    reject(error);
                }
            });
        });
    }

    try {
        // 第一次尝试直接安装依赖
        try {
            await tryInstallDependencies();
        } catch (error) {
            // 如果是npm，直接失败
            if (packageManager === 'npm') {
                if (error.message.includes('命令不存在')) {
                    broadcast(`[DEPS] ❌ npm 不可用，请先安装 Node.js`);
                    broadcast(`[DEPS] Node.js 下载地址: https://nodejs.org/`);
                } else {
                    broadcast(`[DEPS] ❌ npm 安装失败: ${error.message}`);
                    broadcast(`[DEPS] 请检查 Node.js 是否正确安装`);
                }
                throw error;
            }
            
            // 对于pnpm和yarn，检查是否是命令不存在的问题
            if (error.message.includes('命令不存在')) {
                broadcast(`[DEPS] ⚠️  ${packageManager} 未安装，正在尝试自动安装...`);
                
                try {
                    await installPackageManager(packageManager);
                    broadcast(`[DEPS] ✅ ${packageManager} 安装成功，现在重新安装依赖`);
                    
                    // 重新尝试安装依赖
                    await tryInstallDependencies(true);
                } catch (installError) {
                    broadcast(`[DEPS] ❌ 自动安装 ${packageManager} 失败: ${installError.message}`);
                    broadcast(`[DEPS] 请手动安装 ${packageManager}：`);
                    if (packageManager === 'pnpm') {
                        broadcast(`[DEPS] 方法1: npm install -g pnpm`);
                        broadcast(`[DEPS] 方法2: 访问 https://pnpm.io/installation 查看其他安装方式`);
                    } else if (packageManager === 'yarn') {
                        broadcast(`[DEPS] 方法1: npm install -g yarn`);
                        broadcast(`[DEPS] 方法2: 访问 https://yarnpkg.com/getting-started/install 查看其他安装方式`);
                    }
                    throw installError;
                }
            } else {
                // 其他类型的错误（比如网络问题、权限问题等）
                broadcast(`[DEPS] ❌ ${packageManager} 安装过程中出现问题: ${error.message}`);
                throw error;
            }
        }
        
    } catch (error) {
        broadcast(`[DEPS] ❌ 依赖安装失败: ${error.message}`);
        if (currentDepsInstallation) currentDepsInstallation.ended = true;
    }
}

// 安装包管理器函数
async function installPackageManager(packageManager) {
    const broadcast = (log) => {
        broadcastToGitClients({ type: 'deps_log', log });
        console.log(log);
    };

    const broadcastProgress = (progress) => {
        broadcastToGitClients({ type: 'deps_progress', progress });
    };

    return new Promise((resolve, reject) => {
        if (!['pnpm', 'yarn'].includes(packageManager)) {
            return reject(new Error('只支持自动安装 pnpm 和 yarn'));
        }
        
        const installCommand = 'npm';
        const installArgs = ['install', '-g', packageManager];
        
        broadcast(`[DEPS] 执行全局安装: ${installCommand} ${installArgs.join(' ')}`);
        broadcastProgress(25);
        
        const installProcess = spawn(installCommand, installArgs, {
            stdio: 'pipe',
            env: process.env,
            shell: true // 在Windows上很重要，确保可以找到npm命令
        });
        
        let hasOutput = false;
        
        installProcess.stdout.on('data', (data) => {
            hasOutput = true;
            const output = data.toString().trim();
            if (output) {
                broadcast(`[DEPS] ${output}`);
            }
        });
        
        installProcess.stderr.on('data', (data) => {
            const output = data.toString().trim();
            // 只显示重要的错误信息，过滤掉警告
            if (output && !output.includes('WARN') && !output.includes('deprecated') && !output.includes('npm notice')) {
                broadcast(`[DEPS] ${output}`);
            }
        });
        
        installProcess.on('close', (code) => {
            if (code === 0) {
                broadcast(`[DEPS] ✅ ${packageManager} 全局安装成功`);
                broadcastProgress(45);
                resolve();
            } else {
                const errorMsg = hasOutput ? 
                    `全局安装失败，退出代码: ${code}` : 
                    'npm 不可用或网络连接失败';
                broadcast(`[DEPS] ❌ ${packageManager} ${errorMsg}`);
                reject(new Error(`${packageManager} 安装失败: ${errorMsg}`));
            }
        });
        
        installProcess.on('error', (error) => {
            if (error.code === 'ENOENT') {
                broadcast(`[DEPS] ❌ npm 不可用，请先安装 Node.js`);
                broadcast(`[DEPS] Node.js 下载地址: https://nodejs.org/`);
                reject(new Error('npm 不可用，请先安装 Node.js'));
            } else {
                broadcast(`[DEPS] ❌ ${packageManager} 全局安装出错: ${error.message}`);
                reject(error);
            }
        });
    });
}

// 依赖移除状态管理
let currentDepsRemoval = null;

// 移除依赖 API
app.post('/api/git/remove-deps', async (req, res) => {
    if (currentDepsRemoval && !currentDepsRemoval.ended) {
        return res.status(409).json({ success: false, message: '已有依赖移除任务在进行中' });
    }

    try {
        const config = await readGitConfig();
        const repoPath = config.repoPath;
        
        if (!repoPath) {
            return res.status(400).json({ success: false, message: '项目路径未配置' });
        }

        // 检查 node_modules 目录是否存在
        const nodeModulesPath = path.join(repoPath, 'node_modules');
        try {
            await fs.access(nodeModulesPath);
        } catch (error) {
            return res.status(404).json({ success: false, message: 'node_modules 目录不存在' });
        }

        // 开始移除依赖
        removeDependencies(repoPath);
        res.json({ success: true, message: '开始移除依赖' });
        
    } catch (error) {
        res.status(500).json({ success: false, message: error.message });
    }
});

// 取消移除依赖 API
app.post('/api/git/remove-deps/cancel', (req, res) => {
    if (currentDepsRemoval && !currentDepsRemoval.ended && !currentDepsRemoval.cancelled) {
        currentDepsRemoval.cancelled = true;
        
        // 立即尝试终止活跃进程
        if (currentDepsRemoval.activeProcess && !currentDepsRemoval.activeProcess.killed) {
            currentDepsRemoval.activeProcess.kill('SIGTERM');
            broadcastToGitClients({ type: 'remove_deps_log', log: '[REMOVE] 🛑 正在强制终止删除进程...' });
        }
        
        // 清除超时定时器
        if (currentDepsRemoval.timeout) {
            clearTimeout(currentDepsRemoval.timeout);
            currentDepsRemoval.timeout = null;
        }
        
        broadcastToGitClients({ type: 'remove_deps_log', log: '[REMOVE] ❌ 用户请求取消，正在停止操作...' });
        res.json({ success: true, message: '取消移除请求已发送' });
    } else if (currentDepsRemoval && currentDepsRemoval.cancelled) {
        res.json({ success: false, message: '取消请求已在处理中' });
    } else {
        res.json({ success: false, message: '没有正在进行的依赖移除任务' });
    }
});

// 依赖移除函数
async function removeDependencies(repoPath) {
    const broadcast = (log) => {
        broadcastToGitClients({ type: 'remove_deps_log', log });
        console.log(log);
    };

    const broadcastProgress = (progress) => {
        broadcastToGitClients({ type: 'remove_deps_progress', progress });
    };

    const nodeModulesPath = path.join(repoPath, 'node_modules');
    
    broadcast(`[REMOVE] 开始移除 node_modules 目录...`);
    broadcast(`[REMOVE] 项目路径: ${repoPath}`);
    broadcast(`[REMOVE] 目标目录: ${nodeModulesPath}`);
    
    broadcastProgress(10);

    // 创建一个可控制的移除过程对象
    currentDepsRemoval = { 
        ended: false, 
        cancelled: false,
        activeProcess: null,
        timeout: null
    };

    try {
        // 检查目录是否存在
        try {
            await fs.access(nodeModulesPath);
            broadcast(`[REMOVE] ✅ 找到 node_modules 目录`);
        } catch (error) {
            broadcast(`[REMOVE] ❌ node_modules 目录不存在`);
            currentDepsRemoval.ended = true;
            return;
        }

        broadcastProgress(20);

        // 检查是否被取消
        if (currentDepsRemoval.cancelled) {
            broadcast(`[REMOVE] ❌ 移除操作已取消`);
            return;
        }

        // 设置超时机制 (5分钟)
        const timeoutPromise = new Promise((_, reject) => {
            currentDepsRemoval.timeout = setTimeout(() => {
                broadcast(`[REMOVE] ⏰ 删除操作超时 (5分钟)，正在强制结束...`);
                reject(new Error('删除操作超时'));
            }, 5 * 60 * 1000); // 5分钟超时
        });

        // 删除操作的 Promise
        const deletePromise = performDelete(nodeModulesPath, broadcast, broadcastProgress);

        // 使用 Promise.race 来实现超时控制
        try {
            await Promise.race([deletePromise, timeoutPromise]);
        } catch (error) {
            // 清除超时定时器
            if (currentDepsRemoval.timeout) {
                clearTimeout(currentDepsRemoval.timeout);
                currentDepsRemoval.timeout = null;
            }
            
            // 如果有活跃进程，尝试杀死它
            if (currentDepsRemoval.activeProcess && !currentDepsRemoval.activeProcess.killed) {
                currentDepsRemoval.activeProcess.kill('SIGTERM');
                broadcast(`[REMOVE] 🛑 已强制终止删除进程`);
            }
            
            throw error;
        }

        // 清除超时定时器
        if (currentDepsRemoval.timeout) {
            clearTimeout(currentDepsRemoval.timeout);
            currentDepsRemoval.timeout = null;
        }

    } catch (error) {
        if (currentDepsRemoval.cancelled) {
            broadcast(`[REMOVE] ❌ 移除操作已被用户取消`);
        } else {
            broadcast(`[REMOVE] ❌ 移除过程出错: ${error.message}`);
            if (error.code === 'EBUSY' || error.code === 'ENOTEMPTY') {
                broadcast(`[REMOVE] 💡 提示: 目录可能被其他程序占用，请关闭相关程序后重试`);
                broadcast(`[REMOVE] 💡 建议: 关闭 VS Code、WebStorm 等编辑器，然后重试`);
            } else if (error.code === 'EPERM') {
                broadcast(`[REMOVE] 💡 提示: 权限不足，请以管理员身份运行或检查文件权限`);
                broadcast(`[REMOVE] 💡 Windows 解决方案: 右键"以管理员身份运行"程序`);
            } else if (error.message.includes('超时')) {
                broadcast(`[REMOVE] 💡 提示: 删除操作超时，可能是文件过多或系统繁忙`);
                broadcast(`[REMOVE] 💡 建议: 手动删除或重启后重试`);
            }
        }
        
        broadcast(`[REMOVE] 🔄 移除任务已结束，您可以重新尝试`);
    } finally {
        // 确保清理所有资源
        if (currentDepsRemoval.timeout) {
            clearTimeout(currentDepsRemoval.timeout);
        }
        if (currentDepsRemoval.activeProcess && !currentDepsRemoval.activeProcess.killed) {
            currentDepsRemoval.activeProcess.kill('SIGTERM');
        }
        
        currentDepsRemoval.ended = true;
        broadcast(`[REMOVE] 📊 任务状态已重置，可以进行新的操作`);
    }
}

// 执行删除操作的辅助函数
async function performDelete(nodeModulesPath, broadcast, broadcastProgress) {
    broadcast(`[REMOVE] 🗑️ 正在删除 node_modules 目录...`);
    broadcastProgress(30);

    let deleteSuccess = false;
    const isWindows = process.platform === 'win32';
    
    // 方法1: Windows PowerShell 强制删除
    if (isWindows && !deleteSuccess) {
        try {
            broadcast(`[REMOVE] 🔄 使用 PowerShell 强制删除...`);
            
            await new Promise((resolve, reject) => {
                if (currentDepsRemoval.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }

                // PowerShell 强制删除命令，处理只读文件和长路径
                const psCommand = `
                    try {
                        Write-Host "开始强制删除目录..."
                        if (Test-Path "${nodeModulesPath}") {
                            # 递归移除只读属性
                            Get-ChildItem "${nodeModulesPath}" -Recurse -Force | ForEach-Object {
                                if ($_.Attributes -band [System.IO.FileAttributes]::ReadOnly) {
                                    $_.Attributes = $_.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
                                }
                            }
                            # 强制删除
                            Remove-Item "${nodeModulesPath}" -Recurse -Force -ErrorAction Stop
                            Write-Host "删除成功"
                        } else {
                            Write-Host "目录不存在"
                        }
                    } catch {
                        Write-Error $_.Exception.Message
                        exit 1
                    }
                `;
                
                const deleteProcess = spawn('powershell', ['-Command', psCommand], { 
                    shell: true,
                    stdio: 'pipe'
                });
                currentDepsRemoval.activeProcess = deleteProcess;
                
                let errorOutput = '';
                
                const cancelCheckInterval = setInterval(() => {
                    if (currentDepsRemoval.cancelled && !deleteProcess.killed) {
                        clearInterval(cancelCheckInterval);
                        deleteProcess.kill('SIGTERM');
                        reject(new Error('操作已取消'));
                    }
                }, 500);
                
                deleteProcess.stdout.on('data', (data) => {
                    const output = data.toString().trim();
                    if (output) {
                        broadcast(`[REMOVE] PowerShell: ${output}`);
                    }
                });
                
                deleteProcess.stderr.on('data', (data) => {
                    errorOutput += data.toString();
                });
                
                deleteProcess.on('close', (code) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    
                    if (currentDepsRemoval.cancelled) {
                        reject(new Error('操作已取消'));
                        return;
                    }
                    
                    if (code === 0) {
                        resolve();
                    } else {
                        reject(new Error(`PowerShell 执行失败，退出码: ${code}, 错误: ${errorOutput}`));
                    }
                });
                
                deleteProcess.on('error', (error) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    reject(error);
                });
            });
            
            deleteSuccess = true;
            broadcast(`[REMOVE] ✅ PowerShell 强制删除成功`);
            
        } catch (error) {
            if (currentDepsRemoval.cancelled) {
                throw error;
            }
            broadcast(`[REMOVE] ⚠️ PowerShell 删除失败: ${error.message}`);
        }
    }
    
    // 方法2: 使用 robocopy 清空目录 (Windows 特有的强力方法)
    if (isWindows && !deleteSuccess) {
        try {
            broadcast(`[REMOVE] 🔄 使用 robocopy 强制清空目录...`);
            broadcastProgress(50);
            
            // 创建临时空目录
            const tempEmptyDir = path.join(path.dirname(nodeModulesPath), 'temp_empty_' + Date.now());
            await fs.mkdir(tempEmptyDir, { recursive: true });
            
            await new Promise((resolve, reject) => {
                if (currentDepsRemoval.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }

                // robocopy 用空目录覆盖目标目录，然后删除
                const deleteProcess = spawn('robocopy', [tempEmptyDir, nodeModulesPath, '/MIR'], { 
                    shell: true,
                    stdio: 'pipe'
                });
                currentDepsRemoval.activeProcess = deleteProcess;
                
                const cancelCheckInterval = setInterval(() => {
                    if (currentDepsRemoval.cancelled && !deleteProcess.killed) {
                        clearInterval(cancelCheckInterval);
                        deleteProcess.kill('SIGTERM');
                        reject(new Error('操作已取消'));
                    }
                }, 500);
                
                deleteProcess.stdout.on('data', (data) => {
                    broadcast(`[REMOVE] Robocopy: ${data.toString().trim()}`);
                });
                
                deleteProcess.on('close', async (code) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    
                    // 清理临时目录
                    try {
                        await fs.rmdir(tempEmptyDir);
                        await fs.rmdir(nodeModulesPath);
                    } catch (e) {
                        // 忽略清理错误
                    }
                    
                    if (currentDepsRemoval.cancelled) {
                        reject(new Error('操作已取消'));
                        return;
                    }
                    
                    // robocopy 返回码 0-7 都是成功
                    if (code <= 7) {
                        resolve();
                    } else {
                        reject(new Error(`Robocopy 执行失败，退出码: ${code}`));
                    }
                });
                
                deleteProcess.on('error', (error) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    reject(error);
                });
            });
            
            deleteSuccess = true;
            broadcast(`[REMOVE] ✅ Robocopy 清空目录成功`);
            
        } catch (error) {
            if (currentDepsRemoval.cancelled) {
                throw error;
            }
            broadcast(`[REMOVE] ⚠️ Robocopy 删除失败: ${error.message}`);
        }
    }
    
    // 方法3: 传统系统命令
    if (!deleteSuccess) {
        try {
            broadcast(`[REMOVE] 🔄 使用传统系统命令删除...`);
            broadcastProgress(70);
            
            await new Promise((resolve, reject) => {
                if (currentDepsRemoval.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }

                const command = isWindows ? 'rmdir' : 'rm';
                const args = isWindows ? ['/s', '/q', nodeModulesPath] : ['-rf', nodeModulesPath];
                
                const deleteProcess = spawn(command, args, { shell: true });
                currentDepsRemoval.activeProcess = deleteProcess;
                
                let errorOutput = '';
                
                const cancelCheckInterval = setInterval(() => {
                    if (currentDepsRemoval.cancelled && !deleteProcess.killed) {
                        clearInterval(cancelCheckInterval);
                        deleteProcess.kill('SIGTERM');
                        reject(new Error('操作已取消'));
                    }
                }, 500);
                
                deleteProcess.stdout.on('data', (data) => {
                    broadcast(`[REMOVE] ${data.toString().trim()}`);
                });
                
                deleteProcess.stderr.on('data', (data) => {
                    const output = data.toString().trim();
                    if (output && !output.includes('系统找不到指定的路径')) {
                        errorOutput += output;
                    }
                });
                
                deleteProcess.on('close', (code) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    
                    if (currentDepsRemoval.cancelled) {
                        reject(new Error('操作已取消'));
                        return;
                    }
                    
                    if (code === 0) {
                        resolve();
                    } else {
                        reject(new Error(`命令执行失败，退出码: ${code}, 错误: ${errorOutput}`));
                    }
                });
                
                deleteProcess.on('error', (error) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    reject(error);
                });
            });
            
            deleteSuccess = true;
            broadcast(`[REMOVE] ✅ 传统系统命令删除成功`);
            
        } catch (error) {
            if (currentDepsRemoval.cancelled) {
                throw error;
            }
            broadcast(`[REMOVE] ⚠️ 传统系统命令删除失败: ${error.message}`);
        }
    }
    
    // 方法4: Windows 权限强制获取 + 删除 (终极方案)
    if (isWindows && !deleteSuccess) {
        try {
            broadcast(`[REMOVE] 🔄 使用权限强制获取方式删除...`);
            broadcast(`[REMOVE] 💡 正在获取文件所有权并修改权限...`);
            broadcastProgress(75);
            
            // 步骤1: 获取所有权
            await new Promise((resolve, reject) => {
                if (currentDepsRemoval.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }

                const takeownProcess = spawn('takeown', ['/f', nodeModulesPath, '/r', '/d', 'y'], { 
                    shell: true,
                    stdio: 'pipe'
                });
                currentDepsRemoval.activeProcess = takeownProcess;
                
                const cancelCheckInterval = setInterval(() => {
                    if (currentDepsRemoval.cancelled && !takeownProcess.killed) {
                        clearInterval(cancelCheckInterval);
                        takeownProcess.kill('SIGTERM');
                        reject(new Error('操作已取消'));
                    }
                }, 500);
                
                takeownProcess.stdout.on('data', (data) => {
                    broadcast(`[REMOVE] Takeown: ${data.toString().trim()}`);
                });
                
                takeownProcess.on('close', (code) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    
                    if (currentDepsRemoval.cancelled) {
                        reject(new Error('操作已取消'));
                        return;
                    }
                    
                    // takeown 可能会有一些失败，但只要部分成功就继续
                    broadcast(`[REMOVE] ✅ 所有权获取完成 (退出码: ${code})`);
                    resolve();
                });
                
                takeownProcess.on('error', (error) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    broadcast(`[REMOVE] ⚠️ Takeown 执行出错，继续下一步: ${error.message}`);
                    resolve(); // 即使失败也继续
                });
            });
            
            // 步骤2: 修改权限
            await new Promise((resolve, reject) => {
                if (currentDepsRemoval.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }

                const icaclsProcess = spawn('icacls', [nodeModulesPath, '/grant', 'everyone:F', '/t'], { 
                    shell: true,
                    stdio: 'pipe'
                });
                currentDepsRemoval.activeProcess = icaclsProcess;
                
                const cancelCheckInterval = setInterval(() => {
                    if (currentDepsRemoval.cancelled && !icaclsProcess.killed) {
                        clearInterval(cancelCheckInterval);
                        icaclsProcess.kill('SIGTERM');
                        reject(new Error('操作已取消'));
                    }
                }, 500);
                
                icaclsProcess.stdout.on('data', (data) => {
                    broadcast(`[REMOVE] Icacls: ${data.toString().trim()}`);
                });
                
                icaclsProcess.on('close', (code) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    
                    if (currentDepsRemoval.cancelled) {
                        reject(new Error('操作已取消'));
                        return;
                    }
                    
                    broadcast(`[REMOVE] ✅ 权限修改完成 (退出码: ${code})`);
                    resolve();
                });
                
                icaclsProcess.on('error', (error) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    broadcast(`[REMOVE] ⚠️ Icacls 执行出错，继续下一步: ${error.message}`);
                    resolve(); // 即使失败也继续
                });
            });
            
            // 步骤3: 再次尝试 PowerShell 删除
            await new Promise((resolve, reject) => {
                if (currentDepsRemoval.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }

                const psCommand = `Remove-Item "${nodeModulesPath}" -Recurse -Force -ErrorAction SilentlyContinue`;
                const deleteProcess = spawn('powershell', ['-Command', psCommand], { 
                    shell: true,
                    stdio: 'pipe'
                });
                currentDepsRemoval.activeProcess = deleteProcess;
                
                const cancelCheckInterval = setInterval(() => {
                    if (currentDepsRemoval.cancelled && !deleteProcess.killed) {
                        clearInterval(cancelCheckInterval);
                        deleteProcess.kill('SIGTERM');
                        reject(new Error('操作已取消'));
                    }
                }, 500);
                
                deleteProcess.stdout.on('data', (data) => {
                    broadcast(`[REMOVE] PowerShell: ${data.toString().trim()}`);
                });
                
                deleteProcess.on('close', (code) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    
                    if (currentDepsRemoval.cancelled) {
                        reject(new Error('操作已取消'));
                        return;
                    }
                    
                    resolve(); // 不管成功失败都继续
                });
                
                deleteProcess.on('error', (error) => {
                    clearInterval(cancelCheckInterval);
                    currentDepsRemoval.activeProcess = null;
                    resolve(); // 即使失败也继续
                });
            });
            
            deleteSuccess = true;
            broadcast(`[REMOVE] ✅ 权限强制获取方式执行完成`);
            
        } catch (error) {
            if (currentDepsRemoval.cancelled) {
                throw error;
            }
            broadcast(`[REMOVE] ⚠️ 权限强制获取方式失败: ${error.message}`);
        }
    }
    
    // 方法5: Node.js fs.rm 作为最后备用方案
    if (!deleteSuccess) {
        try {
            broadcast(`[REMOVE] 🔄 使用 Node.js 内置方法删除...`);
            broadcastProgress(85);
            
            if (currentDepsRemoval.cancelled) {
                throw new Error('操作已取消');
            }
            
            await fs.rm(nodeModulesPath, { recursive: true, force: true, maxRetries: 2, retryDelay: 1000 });
            deleteSuccess = true;
            broadcast(`[REMOVE] ✅ Node.js 方法删除成功`);
            
        } catch (fsError) {
            if (currentDepsRemoval.cancelled) {
                throw fsError;
            }
            broadcast(`[REMOVE] ⚠️ Node.js 方法删除失败: ${fsError.message}`);
            
            // 如果所有方法都失败了，至少尝试删除一些文件
            broadcast(`[REMOVE] 🔄 尝试部分清理...`);
            try {
                const entries = await fs.readdir(nodeModulesPath);
                let partialSuccess = 0;
                for (const entry of entries.slice(0, 10)) { // 只尝试前10个
                    try {
                        const entryPath = path.join(nodeModulesPath, entry);
                        await fs.rm(entryPath, { recursive: true, force: true });
                        partialSuccess++;
                    } catch (e) {
                        // 忽略单个删除失败
                    }
                }
                if (partialSuccess > 0) {
                    broadcast(`[REMOVE] ✅ 部分清理成功，删除了 ${partialSuccess} 个项目`);
                    deleteSuccess = true; // 标记为部分成功
                }
            } catch (e) {
                broadcast(`[REMOVE] ⚠️ 部分清理也失败了`);
            }
        }
    }

    // 检查是否被取消
    if (currentDepsRemoval.cancelled) {
        throw new Error('操作已取消');
    }

    broadcastProgress(90);

    // 验证删除是否成功
    try {
        await fs.access(nodeModulesPath);
        broadcast(`[REMOVE] ❌ 删除未完全成功，目录仍然存在`);
        broadcast(`[REMOVE] 💡 可能的解决方案:`);
        broadcast(`[REMOVE] 1. 关闭所有编辑器和终端`);
        broadcast(`[REMOVE] 2. 以管理员身份运行程序`);
        broadcast(`[REMOVE] 3. 手动删除剩余文件`);
        deleteSuccess = false;
    } catch (error) {
        // 目录不存在说明删除成功
        if (!deleteSuccess) {
            deleteSuccess = true;
            broadcast(`[REMOVE] ✅ node_modules 目录删除成功`);
        }
        broadcast(`[REMOVE] 💡 提示: 您现在可以重新安装依赖`);
    }

    broadcastProgress(100);
    
    if (deleteSuccess) {
        broadcast(`[REMOVE] 🎉 依赖移除完成`);
    } else {
        broadcast(`[REMOVE] ⚠️ 依赖移除部分完成，请检查剩余文件`);
    }
}

console.log('WebSocket server is set up') 